You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
231 lines
5.2 KiB
231 lines
5.2 KiB
require "email"
|
|
require "option_parser"
|
|
require "baguette-crystal-base"
|
|
|
|
require "crinja"
|
|
|
|
class Context
|
|
class_property simulation : Bool = false
|
|
class_property command : String = "undefined"
|
|
|
|
class_property args = [] of String
|
|
end
|
|
|
|
class Baguette::Configuration
|
|
class Mailer < Base
|
|
# Default mailer: 127.0.0.1, not "localhost" for glibc fuckery reasons.
|
|
# Without domain name resolution, one can statically compile the mailer
|
|
# for machines without glibc (such as on alpine linux).
|
|
property smtpd_host : String = "127.0.0.1"
|
|
property smtpd_port : UInt16 = 25
|
|
# "your.host.example.com"
|
|
property smtpd_helo_domain : String = "localhost"
|
|
|
|
property verbosity : Int32 = 3
|
|
|
|
# template => subject
|
|
property templates : YAML::Any? = nil
|
|
property templates_directory : String = "/etc/baguette/mailer/templates/"
|
|
|
|
def initialize
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
class OptionParser
|
|
def to_s(io : IO)
|
|
if banner = @banner
|
|
io << banner
|
|
io << "\n\n"
|
|
end
|
|
@flags.join io, "\n"
|
|
end
|
|
end
|
|
|
|
opt_simulation = -> (parser : OptionParser) {
|
|
parser.on "-s", "--simulation", "Simulation: do not do anything." do
|
|
Context.simulation = true
|
|
Baguette::Log.info "Simulation."
|
|
end
|
|
}
|
|
|
|
unrecognized_args_to_context_args = -> (parser : OptionParser,
|
|
nexact : Int32?,
|
|
at_least : Int32?) {
|
|
|
|
# With the right args, these will be interpreted as serialized data.
|
|
parser.unknown_args do |args|
|
|
|
|
# either we test with the exact expected number of arguments or the least.
|
|
if exact = nexact
|
|
if args.size != exact
|
|
Baguette::Log.error "#{parser}"
|
|
exit 1
|
|
end
|
|
elsif least = at_least
|
|
if args.size < least
|
|
Baguette::Log.error "#{parser}"
|
|
exit 1
|
|
end
|
|
else
|
|
Baguette::Log.error "Number of parameters not even provided!"
|
|
Baguette::Log.error "#{parser}"
|
|
exit 1
|
|
end
|
|
|
|
args.each do |arg|
|
|
Baguette::Log.debug "Unrecognized argument: #{arg} (adding to Context.args)"
|
|
if Context.args.nil?
|
|
Context.args = Array(String).new
|
|
end
|
|
Context.args.not_nil! << arg
|
|
end
|
|
end
|
|
}
|
|
|
|
opt_help = -> (parser : OptionParser) {
|
|
parser.on "-h", "--help", "Prints command usage." do
|
|
puts "usage: #{PROGRAM_NAME} command -h"
|
|
puts
|
|
puts parser
|
|
|
|
case Context.command
|
|
when /send/
|
|
#ENUM.names.each do |n|
|
|
# Baguette::Log.warning "- #{n}"
|
|
#end
|
|
end
|
|
|
|
exit 0
|
|
end
|
|
}
|
|
|
|
|
|
parser = OptionParser.new do |parser|
|
|
parser.banner = "Mailer."
|
|
parser.on "-v verbosity", "--verbosity v", "Verbosity. From 0 to 4 (debug)." do |v|
|
|
Baguette::Context.verbosity = v.to_i
|
|
Baguette::Log.info "verbosity = #{v}"
|
|
end
|
|
|
|
opt_simulation.call parser
|
|
|
|
parser.on("send", "Send an email.") do
|
|
parser.banner = "usage: send <template> <email>"
|
|
Baguette::Log.info "sending an email"
|
|
Context.command = "send"
|
|
unrecognized_args_to_context_args.call parser, nil, 2
|
|
end
|
|
|
|
opt_help.call parser
|
|
end
|
|
|
|
parser.parse
|
|
|
|
class Actions
|
|
property the_call = {} of String => Proc(Nil)
|
|
property mailer_configuration : Baguette::Configuration::Mailer
|
|
property smtpd_client : EMail::Client
|
|
|
|
def initialize(@mailer_configuration, @smtpd_client)
|
|
@the_call["send"] = ->send
|
|
end
|
|
|
|
def env_params
|
|
{
|
|
"from" => ENV["FROM"],
|
|
"subject" => ENV["SUBJECT"],
|
|
}
|
|
rescue e
|
|
Baguette::Log.error "#{e}"
|
|
exit 1
|
|
end
|
|
|
|
# One-shot email transfer.
|
|
def send
|
|
template, to = Context.args.not_nil!
|
|
Baguette::Log.info "sending #{template} to #{to}"
|
|
|
|
template_parameters = if t = @mailer_configuration.templates
|
|
if t[template]?
|
|
{
|
|
"from" => t[template]["from"].to_s,
|
|
"subject" => t[template]["subject"].to_s,
|
|
}
|
|
else
|
|
env_params
|
|
end
|
|
else
|
|
env_params
|
|
end
|
|
|
|
env = Crinja.new
|
|
env.loader = Crinja::Loader::FileSystemLoader.new @mailer_configuration.templates_directory
|
|
|
|
# Getting 'subject' and 'from' through an environment variable or configuration, else raise.
|
|
subject = template_parameters["subject"]
|
|
from = template_parameters["from"]
|
|
|
|
# Allow to rewrite the destination.
|
|
to = ENV["TO"]? || to
|
|
|
|
template = env.get_template "#{template}.j2"
|
|
|
|
if Context.simulation
|
|
Baguette::Log.info "(simulation) email to send should be:"
|
|
Baguette::Log.info "subject: #{subject}"
|
|
Baguette::Log.info "from: #{from}"
|
|
Baguette::Log.info "to: #{to}"
|
|
Baguette::Log.info "content: #{template.render ENV}"
|
|
else
|
|
content = template.render ENV
|
|
|
|
email = EMail::Message.new
|
|
email.from from
|
|
email.to to
|
|
email.subject subject
|
|
email.message content
|
|
|
|
smtpd_client.start do
|
|
# In this block, default receiver is client
|
|
send(email)
|
|
end
|
|
end
|
|
rescue e
|
|
Baguette::Log.error "#{e}"
|
|
exit 1
|
|
end
|
|
end
|
|
|
|
def main
|
|
|
|
mail_conf = if c = Baguette::Configuration::Mailer.get
|
|
c
|
|
else
|
|
Baguette::Configuration::Mailer.new
|
|
end
|
|
|
|
if Context.simulation
|
|
pp! mail_conf
|
|
end
|
|
|
|
# Now we did read the intent, we should proceed doing what was asked.
|
|
begin
|
|
smtpd_config = EMail::Client::Config.new mail_conf.smtpd_host,
|
|
mail_conf.smtpd_port.to_i,
|
|
helo_domain: mail_conf.smtpd_helo_domain
|
|
smtpd_client = EMail::Client.new smtpd_config
|
|
actions = Actions.new mail_conf, smtpd_client
|
|
|
|
actions.the_call[Context.command].call
|
|
rescue e
|
|
Baguette::Log.info "The command is not recognized (or implemented): #{e}."
|
|
end
|
|
|
|
rescue e
|
|
Baguette::Log.error "Exception: #{e}"
|
|
end
|
|
|
|
main
|