Checkpoint. Code layout.
FossilOrigin-Name: 8a2deb5ee3deb752e25c82f50801529de8e28ec95dd9d4f292046bfc09f9dcd2
This commit is contained in:
parent
3583868771
commit
024b108bed
7 changed files with 330 additions and 51 deletions
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
|
||||
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue