Checkpoint. Code layout.
FossilOrigin-Name: 8a2deb5ee3deb752e25c82f50801529de8e28ec95dd9d4f292046bfc09f9dcd2
This commit is contained in:
parent
3583868771
commit
024b108bed
7 changed files with 330 additions and 51 deletions
|
|
@ -1,2 +1,3 @@
|
|||
Session.vim
|
||||
.cache/*
|
||||
docs/*
|
||||
|
|
|
|||
6
Makefile
6
Makefile
|
|
@ -12,17 +12,19 @@ dependencies:
|
|||
development: ${FILES}
|
||||
# can use gdb with this...
|
||||
nim --debugInfo --assertions:on --linedir:on -d:testing -d:nimTypeNames --nimcache:.cache c ${FILES}
|
||||
mv src/sieb .
|
||||
|
||||
debugger: ${FILES}
|
||||
nim --debugger:on --nimcache:.cache c ${FILES}
|
||||
mv src/sieb .
|
||||
|
||||
release:dependencies ${FILES}
|
||||
nim -d:release -d:nimDebugDlOpen --opt:speed --parallelBuild:0 --nimcache:.cache c ${FILES}
|
||||
nim -d:release -d:strip --passc:-flto --opt:speed --nimcache:.cache c ${FILES}
|
||||
mv src/sieb .
|
||||
|
||||
docs:
|
||||
nim doc ${FILES}
|
||||
#nim buildIndex ${FILES}
|
||||
mv src/htmldocs docs
|
||||
|
||||
clean:
|
||||
fossil clean --dotfiles -f -v
|
||||
|
|
|
|||
47
config.yml
47
config.yml
|
|
@ -2,47 +2,40 @@
|
|||
# Example Sieb configuration file.
|
||||
#
|
||||
|
||||
|
||||
# Default, no logging. Relative to homedir.
|
||||
logfile: sieb.log
|
||||
#logfile: sieb.log
|
||||
|
||||
## Filter message before rules
|
||||
#pre_filter:
|
||||
# - bogofilter
|
||||
pre_filter:
|
||||
- bogofilter
|
||||
|
||||
## Filter message after rules
|
||||
#post_filter:
|
||||
# - bogofilter
|
||||
|
||||
rules:
|
||||
-
|
||||
headers:
|
||||
woo: yeah
|
||||
|
||||
|
||||
## Ordered, top down, first match wins.
|
||||
## Headers are lowercased. Multiple matches are AND'ed.
|
||||
##
|
||||
## Delivery default is ~/Maildir, any set value is an auto-created maildir under
|
||||
## that path.
|
||||
##
|
||||
#rules:
|
||||
# -
|
||||
# headers:
|
||||
# x-what: pcre-matcher
|
||||
# poonie: pcre-matcher
|
||||
# deliver: .whatever
|
||||
rules:
|
||||
-
|
||||
headers:
|
||||
x-what: pcre-matcher
|
||||
poonie: pcre-matcher
|
||||
deliver: .whatever
|
||||
|
||||
# # Magic "TO" which means To: OR Cc:
|
||||
# -
|
||||
# headers:
|
||||
# TO: regexp
|
||||
# deliver: .whereever
|
||||
|
||||
# # Filter message through reformail, then deliver to ~/Maildir.
|
||||
# -
|
||||
# headers:
|
||||
# x-what: fuckery
|
||||
# filter: reformail ...
|
||||
# # Magic "TO" which means To: OR Cc:
|
||||
# -
|
||||
# headers:
|
||||
# TO: regexp
|
||||
# deliver: .whereever
|
||||
|
||||
# # Filter message through reformail, then deliver to ~/Maildir.
|
||||
# -
|
||||
# headers:
|
||||
# x-what: fuckery
|
||||
# filter: reformail ...
|
||||
|
||||
|
||||
|
|
|
|||
87
src/lib/config.nim
Normal file
87
src/lib/config.nim
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
# vim: set et nosta sw=4 ts=4 :
|
||||
|
||||
#
|
||||
# Methods for finding and parsing sieb rules from YAML.
|
||||
#
|
||||
|
||||
#############################################################
|
||||
# I M P O R T S
|
||||
#############################################################
|
||||
|
||||
import
|
||||
std/os,
|
||||
std/streams,
|
||||
std/strformat,
|
||||
std/tables,
|
||||
yaml/parser,
|
||||
yaml/serialization
|
||||
|
||||
import util
|
||||
|
||||
|
||||
#############################################################
|
||||
# C O N S T A N T S
|
||||
#############################################################
|
||||
|
||||
const CONFFILES = @[
|
||||
"/usr/local/etc/sieb/config.yml",
|
||||
"/etc/sieb/config.yml"
|
||||
]
|
||||
|
||||
|
||||
#############################################################
|
||||
# T Y P E S
|
||||
#############################################################
|
||||
|
||||
type
|
||||
rule = object
|
||||
headers {.defaultVal: initTable[string, string]()}: Table[ string, string ]
|
||||
deliver {.defaultVal: "Maildir"}: string
|
||||
filter {.defaultVal: ""}: string
|
||||
|
||||
# Typed configuration file layout for YAML loading.
|
||||
Config* = object
|
||||
logfile {.defaultVal: "".}: string
|
||||
pre_filter {.defaultVal: @[]}: seq[string]
|
||||
post_filter {.defaultVal: @[]}: seq[string]
|
||||
rules {.defaultVal: @[]}: seq[rule]
|
||||
|
||||
|
||||
#############################################################
|
||||
# M E T H O D S
|
||||
#############################################################
|
||||
|
||||
proc parse( path: string ): Config =
|
||||
## Return a parsed configuration from yaml.
|
||||
debug "Using configuration at: {path}".fmt
|
||||
let stream = newFileStream( path )
|
||||
try:
|
||||
stream.load( result )
|
||||
except YamlParserError as err:
|
||||
debug err.msg
|
||||
return Config() # return empty default, it could be "half parsed"
|
||||
except YamlConstructionError as err:
|
||||
debug err.msg
|
||||
return Config()
|
||||
finally:
|
||||
stream.close
|
||||
|
||||
|
||||
proc get_config*( path: string ): Config =
|
||||
## Choose a configuration file for parsing, or if there are
|
||||
## none available, return an empty config.
|
||||
if path != "":
|
||||
if not path.fileExists:
|
||||
debug "Configfile \"{path}\" unreadable, ignoring.".fmt
|
||||
return
|
||||
return parse( path )
|
||||
|
||||
else:
|
||||
# No explicit path given, walk the hardcoded paths to
|
||||
# try and find one.
|
||||
let homeconf = @[ getConfigDir() & "sieb/config.yml" ]
|
||||
let configs = homeconf & CONFFILES
|
||||
for conf in configs:
|
||||
if conf.fileExists:
|
||||
return parse( conf )
|
||||
|
||||
79
src/lib/maildir.nim
Normal file
79
src/lib/maildir.nim
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# vim: set et nosta sw=4 ts=4 :
|
||||
#
|
||||
# A class that represents an individual Maildir.
|
||||
#
|
||||
|
||||
#############################################################
|
||||
# I M P O R T S
|
||||
#############################################################
|
||||
|
||||
import
|
||||
std/os,
|
||||
std/streams,
|
||||
std/strformat,
|
||||
std/times
|
||||
|
||||
import
|
||||
util
|
||||
|
||||
|
||||
#############################################################
|
||||
# T Y P E S
|
||||
#############################################################
|
||||
|
||||
# A Maildir object.
|
||||
#
|
||||
type Maildir* = ref object
|
||||
path*: string # Absolute path to the encapsualting dir
|
||||
cur: string
|
||||
new: string
|
||||
tmp: string
|
||||
|
||||
# An email message, under a specific Maildir.
|
||||
#
|
||||
type Message* = ref object
|
||||
dir: Maildir
|
||||
path: string
|
||||
stream: FileStream
|
||||
|
||||
|
||||
#############################################################
|
||||
# M E T H O D S
|
||||
#############################################################
|
||||
|
||||
proc newMaildir*( path: string ): Maildir =
|
||||
## Create and return a new Maildir object, making it on-disk if necessary.
|
||||
result = new Maildir
|
||||
result.path = path
|
||||
result.cur = path & "/cur"
|
||||
result.new = path & "/new"
|
||||
result.tmp = path & "/tmp"
|
||||
|
||||
if not dirExists( path ):
|
||||
let perms = { fpUserExec, fpUserWrite, fpUserRead }
|
||||
debug "Creating new maildir at {path}.".fmt
|
||||
try:
|
||||
for p in [ result.path, result.cur, result.new, result.tmp ]:
|
||||
p.createDir
|
||||
p.setFilePermissions( perms )
|
||||
|
||||
except CatchableError as err:
|
||||
deferral "Unable to create Maildir: ({err.msg}), deferring delivery.".fmt
|
||||
|
||||
|
||||
proc newMessage*( dir: Maildir ): Message =
|
||||
## Create and return a Message - an open FileStream under a specific Maildir
|
||||
## (in tmp)
|
||||
result = new Message
|
||||
|
||||
let now = getTime()
|
||||
result.dir = dir
|
||||
echo now
|
||||
|
||||
# result.path = dir.path & now.seconds
|
||||
|
||||
|
||||
|
||||
# make new message (tmp)
|
||||
# save message (move from tmp to new)
|
||||
|
||||
115
src/lib/util.nim
Normal file
115
src/lib/util.nim
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
# vim: set et nosta sw=4 ts=4 :
|
||||
#
|
||||
# Various helper functions that don't have a better landing spot.
|
||||
#
|
||||
|
||||
#############################################################
|
||||
# I M P O R T S
|
||||
#############################################################
|
||||
|
||||
import
|
||||
std/parseopt,
|
||||
std/terminal,
|
||||
std/strutils
|
||||
|
||||
|
||||
#############################################################
|
||||
# C O N S T A N T S
|
||||
#############################################################
|
||||
|
||||
const
|
||||
VERSION = "v0.1.0"
|
||||
USAGE = """
|
||||
./sieb [-c] [-d] [-h] [-v]
|
||||
|
||||
-c --conf:
|
||||
Use a specific configuration file. Otherwise, files are
|
||||
attempted in the following order:
|
||||
- ~/.config/sieb/config.yml
|
||||
- /usr/local/etc/sieb/config.yml
|
||||
- /etc/sieb/config.yml
|
||||
|
||||
-d --debug:
|
||||
Debug: Be verbose while parsing.
|
||||
|
||||
-h --help:
|
||||
Help. You're lookin' at it.
|
||||
|
||||
-v --version:
|
||||
Display version number.
|
||||
"""
|
||||
|
||||
#############################################################
|
||||
# T Y P E S
|
||||
#############################################################
|
||||
|
||||
type Opts = object
|
||||
config*: string # The path to an explicit configuration file.
|
||||
debug*: bool # Explain what's being done.
|
||||
|
||||
|
||||
#############################################################
|
||||
# M E T H O D S
|
||||
#############################################################
|
||||
|
||||
proc hl( msg: string, fg: ForegroundColor, bright=false ): string =
|
||||
## Quick wrapper for color formatting a string, since the 'terminal'
|
||||
## module only deals with stdout directly.
|
||||
if not isatty(stdout): return msg
|
||||
|
||||
var color: BiggestInt = ord( fg )
|
||||
if bright: inc( color, 60 )
|
||||
result = "\e[" & $color & 'm' & msg & "\e[0m"
|
||||
|
||||
|
||||
proc deferral*( msg: string ) =
|
||||
## Exit with Qmail deferral code immediately.
|
||||
echo msg.replace( "\n", " - " ).hl( fgRed, bright=true )
|
||||
quit( 1 )
|
||||
|
||||
|
||||
proc debug*( msg: string ) =
|
||||
## Emit +msg+ if debug mode is enabled.
|
||||
if defined( testing ): echo msg
|
||||
|
||||
|
||||
proc parse_cmdline*: Opts =
|
||||
## Populate the opts object with the user's preferences.
|
||||
|
||||
# Config object defaults.
|
||||
#
|
||||
result = Opts(
|
||||
config: "",
|
||||
debug: false
|
||||
)
|
||||
|
||||
# always set debug mode if development build.
|
||||
result.debug = defined( testing )
|
||||
|
||||
for kind, key, val in getopt():
|
||||
case kind
|
||||
|
||||
of cmdArgument:
|
||||
discard
|
||||
|
||||
of cmdLongOption, cmdShortOption:
|
||||
case key
|
||||
of "conf", "c":
|
||||
result.config = val
|
||||
|
||||
of "debug", "d":
|
||||
result.debug = true
|
||||
|
||||
of "help", "h":
|
||||
echo USAGE
|
||||
quit( 0 )
|
||||
|
||||
of "version", "v":
|
||||
echo "Sieb " & VERSION
|
||||
quit( 0 )
|
||||
|
||||
else: discard
|
||||
|
||||
of cmdEnd: assert( false ) # shouldn't reach here
|
||||
|
||||
|
||||
46
src/sieb.nim
46
src/sieb.nim
|
|
@ -1,31 +1,33 @@
|
|||
# vim: set et nosta sw=4 ts=4 :
|
||||
|
||||
import
|
||||
std/streams,
|
||||
yaml/serialization
|
||||
std/os,
|
||||
std/streams
|
||||
|
||||
const
|
||||
VERSION = "v0.1.0"
|
||||
|
||||
type
|
||||
rule = object
|
||||
headers: seq[ tuple[ header: string, regexp: string ] ]
|
||||
deliver {.defaultVal: "Maildir"}: string
|
||||
filter {.defaultVal: ""}: string
|
||||
|
||||
# Typed configuration file layout for YAML loading.
|
||||
Config = object
|
||||
logfile {.defaultVal: "".}: string
|
||||
pre_filter {.defaultVal: @[]}: seq[string]
|
||||
post_filter {.defaultVal: @[]}: seq[string]
|
||||
rules {.defaultVal: @[]}: seq[rule]
|
||||
import
|
||||
lib/config,
|
||||
lib/maildir,
|
||||
lib/util
|
||||
|
||||
|
||||
var conf: Config
|
||||
if not existsEnv( "HOME" ):
|
||||
deferral "Unable to determine HOME from environment."
|
||||
|
||||
let s = newFileStream( "config.yml" )
|
||||
load( s, conf )
|
||||
s.close
|
||||
let
|
||||
home = getHomeDir()
|
||||
opts = parse_cmdline()
|
||||
conf = get_config( opts.config )
|
||||
default = newMaildir( home & "Maildir" )
|
||||
|
||||
echo conf
|
||||
|
||||
echo repr default.newMessage
|
||||
|
||||
# let input = stdin.newFileStream()
|
||||
# var buf = input.readStr( 8192 )
|
||||
# var message = buf
|
||||
# while buf != "":
|
||||
# buf = input.readStr( 8192 )
|
||||
# message = message & buf
|
||||
|
||||
# echo message
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue