Got message filtering working.
FossilOrigin-Name: 47c9a66b9c547f9af30bebd226b757d475f616b976baf315faae653a01cdf97a
This commit is contained in:
parent
312de83456
commit
105b21d9f7
4 changed files with 105 additions and 37 deletions
|
|
@ -7,7 +7,8 @@
|
||||||
|
|
||||||
## Filter message before rules
|
## Filter message before rules
|
||||||
pre_filter:
|
pre_filter:
|
||||||
- bogofilter
|
- 'reformail -A "X-Sieb: Pre-filtered!"'
|
||||||
|
# - bogofilter
|
||||||
|
|
||||||
## Filter message after rules
|
## Filter message after rules
|
||||||
#post_filter:
|
#post_filter:
|
||||||
|
|
|
||||||
|
|
@ -41,10 +41,10 @@ type
|
||||||
|
|
||||||
# Typed configuration file layout for YAML loading.
|
# Typed configuration file layout for YAML loading.
|
||||||
Config* = object
|
Config* = object
|
||||||
logfile {.defaultVal: "".}: string
|
logfile* {.defaultVal: "".}: string
|
||||||
pre_filter {.defaultVal: @[]}: seq[string]
|
pre_filter* {.defaultVal: @[]}: seq[string]
|
||||||
post_filter {.defaultVal: @[]}: seq[string]
|
post_filter* {.defaultVal: @[]}: seq[string]
|
||||||
rules {.defaultVal: @[]}: seq[rule]
|
rules* {.defaultVal: @[]}: seq[rule]
|
||||||
|
|
||||||
|
|
||||||
#############################################################
|
#############################################################
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,8 @@ import
|
||||||
const
|
const
|
||||||
OWNERDIRPERMS = { fpUserExec, fpUserWrite, fpUserRead }
|
OWNERDIRPERMS = { fpUserExec, fpUserWrite, fpUserRead }
|
||||||
OWNERFILEPERMS = { fpUserWrite, fpUserRead }
|
OWNERFILEPERMS = { fpUserWrite, fpUserRead }
|
||||||
|
# FILTERPROCOPTS = { poUsePath }
|
||||||
|
FILTERPROCOPTS = { poUsePath, poEvalCommand }
|
||||||
BUFSIZE = 8192 # reading and writing buffer size
|
BUFSIZE = 8192 # reading and writing buffer size
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -52,20 +54,28 @@ type Message* = ref object
|
||||||
stream: FileStream
|
stream: FileStream
|
||||||
|
|
||||||
|
|
||||||
|
# Count messages generated during a run.
|
||||||
|
var msgcount = 0
|
||||||
|
|
||||||
|
|
||||||
#############################################################
|
#############################################################
|
||||||
# M E T H O D S
|
# M E T H O D S
|
||||||
#############################################################
|
#############################################################
|
||||||
|
|
||||||
|
#------------------------------------------------------------
|
||||||
|
# Maildir
|
||||||
|
#------------------------------------------------------------
|
||||||
|
|
||||||
proc newMaildir*( path: string ): Maildir =
|
proc newMaildir*( path: string ): Maildir =
|
||||||
## Create and return a new Maildir object, making it on-disk if necessary.
|
## Create and return a new Maildir object, making it on-disk if necessary.
|
||||||
result = new Maildir
|
result = new Maildir
|
||||||
result.path = path
|
result.path = path
|
||||||
result.cur = path & "/cur"
|
result.cur = joinPath( path, "cur" )
|
||||||
result.new = path & "/new"
|
result.new = joinPath( path, "new" )
|
||||||
result.tmp = path & "/tmp"
|
result.tmp = joinPath( path, "tmp" )
|
||||||
|
|
||||||
if not dirExists( path ):
|
if not dirExists( path ):
|
||||||
debug "Creating new maildir at {path}.".fmt
|
debug "Creating new maildir at {path}".fmt
|
||||||
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
|
||||||
|
|
@ -80,6 +90,10 @@ proc subDir*( dir: Maildir, path: string ): Maildir =
|
||||||
result = newMaildir( dir.path & "/" & path )
|
result = newMaildir( dir.path & "/" & path )
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------
|
||||||
|
# Message
|
||||||
|
#------------------------------------------------------------
|
||||||
|
|
||||||
proc newMessage*( dir: Maildir ): Message =
|
proc newMessage*( dir: Maildir ): Message =
|
||||||
## Create and return a Message - an open FileStream under a specific Maildir
|
## Create and return a Message - an open FileStream under a specific Maildir
|
||||||
## (in tmp)
|
## (in tmp)
|
||||||
|
|
@ -89,25 +103,31 @@ proc newMessage*( dir: Maildir ): Message =
|
||||||
var hostname = newString(256)
|
var hostname = newString(256)
|
||||||
discard getHostname( cstring(hostname), cint(256) )
|
discard getHostname( cstring(hostname), cint(256) )
|
||||||
|
|
||||||
|
msgcount = msgcount + 1
|
||||||
result.dir = dir
|
result.dir = dir
|
||||||
result.basename = $now.toUnixFloat() & '.' & $getCurrentProcessID() & '.' & $hostname
|
result.basename = $now.toUnixFloat & '.' & $getCurrentProcessID() & '.' & $msgcount & '.' & $hostname
|
||||||
result.path = result.dir.tmp & "/" & result.basename
|
result.path = joinPath( result.dir.tmp, result.basename )
|
||||||
result.headers = @[]
|
result.headers = @[]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
debug "Opening new message at {result.path}.".fmt
|
debug "Opening new message at {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:
|
||||||
deferral "Unable to write file {result.path}: {err.msg}".fmt
|
deferral "Unable to write file {result.path} {err.msg}".fmt
|
||||||
|
|
||||||
|
|
||||||
|
proc open*( msg: Message ) =
|
||||||
|
## Open (or re-open) a Message file stream.
|
||||||
|
msg.stream = msg.path.openFileStream
|
||||||
|
|
||||||
|
|
||||||
proc save*( msg: Message, dir=msg.dir ) =
|
proc save*( msg: Message, dir=msg.dir ) =
|
||||||
## Move the message from tmp to new. Defaults to its current
|
## Move the message from tmp to new. Defaults to its current
|
||||||
## maildir, but can be provided a different one.
|
## maildir, but can be provided a different one.
|
||||||
msg.stream.close()
|
msg.stream.close
|
||||||
let newpath = dir.new & "/" & msg.basename
|
let newpath = joinPath( dir.new, msg.basename )
|
||||||
debug "Moving message to {newpath}.".fmt
|
debug "Delivering message to {newpath}".fmt
|
||||||
msg.path.moveFile( newpath )
|
msg.path.moveFile( newpath )
|
||||||
msg.dir = dir
|
msg.dir = dir
|
||||||
msg.path = newpath
|
msg.path = newpath
|
||||||
|
|
@ -115,8 +135,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
|
debug "Removing message at {msg.path}".fmt
|
||||||
msg.path.removeFile
|
msg.path.removeFile
|
||||||
msg.path = ""
|
msg.path = ""
|
||||||
|
|
||||||
|
|
@ -124,7 +144,7 @@ proc delete*( msg: Message ) =
|
||||||
proc writeStdin*( msg: Message ) =
|
proc writeStdin*( msg: Message ) =
|
||||||
## Streams stdin to the message file, returning how
|
## Streams stdin to the message file, returning how
|
||||||
## many bytes were written.
|
## many bytes were written.
|
||||||
let input = stdin.newFileStream()
|
let input = stdin.newFileStream
|
||||||
var buf = input.readStr( BUFSIZE )
|
var buf = input.readStr( BUFSIZE )
|
||||||
var total = buf.len
|
var total = buf.len
|
||||||
msg.stream.write( buf )
|
msg.stream.write( buf )
|
||||||
|
|
@ -133,20 +153,65 @@ proc writeStdin*( msg: Message ) =
|
||||||
buf = input.readStr( BUFSIZE )
|
buf = input.readStr( BUFSIZE )
|
||||||
total = total + buf.len
|
total = total + buf.len
|
||||||
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 from stdin".fmt
|
||||||
|
|
||||||
|
|
||||||
# FIXME: filter through external program
|
proc filter*( orig_msg: Message, cmd: string ): Message =
|
||||||
# - open new message
|
## Filter message content through an external program,
|
||||||
# - stream current message to new
|
## returning a new Message if successful.
|
||||||
# - remove current
|
try:
|
||||||
# - return new
|
var buf: string
|
||||||
#
|
|
||||||
|
# let command = cmd.split
|
||||||
|
# let process = command[0].startProcess(
|
||||||
|
# args = command[1..(command.len-1)],
|
||||||
|
# options = FILTERPROCOPTS
|
||||||
|
# )
|
||||||
|
|
||||||
|
let process = cmd.startProcess( options = FILTERPROCOPTS )
|
||||||
|
|
||||||
|
# Read from the original message, write to the filter
|
||||||
|
# process in chunks.
|
||||||
|
#
|
||||||
|
orig_msg.open
|
||||||
|
buf = orig_msg.stream.readStr( BUFSIZE )
|
||||||
|
process.inputStream.write( buf )
|
||||||
|
process.inputStream.flush
|
||||||
|
while buf != "" and buf.len == BUFSIZE:
|
||||||
|
buf = orig_msg.stream.readStr( BUFSIZE )
|
||||||
|
process.inputStream.write( buf )
|
||||||
|
process.inputStream.flush
|
||||||
|
|
||||||
|
# Read from the filter process until EOF, send to the
|
||||||
|
# new message in chunks.
|
||||||
|
process.inputStream.close
|
||||||
|
let new_msg = newMessage( orig_msg.dir )
|
||||||
|
buf = process.outputStream.readStr( BUFSIZE )
|
||||||
|
new_msg.stream.write( buf )
|
||||||
|
new_msg.stream.flush
|
||||||
|
while buf != "" and buf.len == BUFSIZE:
|
||||||
|
buf = process.outputStream.readStr( BUFSIZE )
|
||||||
|
new_msg.stream.write( buf )
|
||||||
|
new_msg.stream.flush
|
||||||
|
|
||||||
|
let exitcode = process.waitForExit
|
||||||
|
debug "Filter exited: {exitcode}".fmt
|
||||||
|
process.close
|
||||||
|
orig_msg.delete
|
||||||
|
result = new_msg
|
||||||
|
|
||||||
|
except OSError as err:
|
||||||
|
debug "Unable to filter message: {err.msg}".fmt
|
||||||
|
result = orig_msg
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# FIXME: header parsing to tuples
|
# FIXME: header parsing to tuples
|
||||||
# - open file
|
# - open file
|
||||||
# - skip lines that don't match headers
|
# - skip lines that don't match headers
|
||||||
# - unwrap multiline headers
|
# - unwrap multiline headers
|
||||||
# - store header, add value to seq of strings
|
# - store header, add value to seq of strings
|
||||||
|
|
||||||
|
|
||||||
22
src/sieb.nim
22
src/sieb.nim
|
|
@ -1,14 +1,16 @@
|
||||||
# vim: set et nosta sw=4 ts=4 :
|
# vim: set et nosta sw=4 ts=4 :
|
||||||
|
|
||||||
import
|
import
|
||||||
std/os
|
std/os,
|
||||||
|
std/strformat
|
||||||
|
|
||||||
import
|
import
|
||||||
lib/config,
|
lib/config,
|
||||||
lib/maildir,
|
lib/message,
|
||||||
lib/util
|
lib/util
|
||||||
|
|
||||||
|
|
||||||
|
# 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."
|
||||||
|
|
||||||
|
|
@ -16,13 +18,13 @@ let
|
||||||
home = getHomeDir()
|
home = getHomeDir()
|
||||||
opts = parse_cmdline()
|
opts = parse_cmdline()
|
||||||
conf = get_config( opts.config )
|
conf = get_config( opts.config )
|
||||||
default = newMaildir( home & "Maildir" )
|
default = newMaildir( joinPath( home, "Maildir" ) )
|
||||||
|
|
||||||
let dest = default.subDir( ".wooo" )
|
|
||||||
|
|
||||||
let msg = default.newMessage
|
|
||||||
msg.writeStdin()
|
|
||||||
# msg.filter()
|
|
||||||
msg.save( dest )
|
|
||||||
|
|
||||||
|
# let dest = default.subDir( "woo" )
|
||||||
|
var msg = default.newMessage
|
||||||
|
msg.writeStdin
|
||||||
|
for filter in conf.pre_filter:
|
||||||
|
debug "Running pre-filter: {filter}".fmt
|
||||||
|
msg = msg.filter( filter )
|
||||||
|
msg.save()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue