mirror of
https://blitiri.com.ar/repos/chasquid
synced 2025-12-17 14:37:02 +00:00
courier: Let the users configure the mail delivery agent
This patch adds configuration options for the MDA binary and command line arguments, and changes the (soon to be renamed) procmail courier to make use of them.
This commit is contained in:
@@ -51,6 +51,9 @@ func main() {
|
||||
go http.ListenAndServe(conf.MonitoringAddress, nil)
|
||||
}
|
||||
|
||||
courier.MailDeliveryAgentBin = conf.MailDeliveryAgentBin
|
||||
courier.MailDeliveryAgentArgs = conf.MailDeliveryAgentArgs
|
||||
|
||||
s := NewServer()
|
||||
s.Hostname = conf.Hostname
|
||||
s.MaxDataSize = conf.MaxDataSizeMb * 1024 * 1024
|
||||
|
||||
@@ -47,6 +47,14 @@ func Load(path string) (*Config, error) {
|
||||
c.Address = append(c.Address, "systemd")
|
||||
}
|
||||
|
||||
if c.MailDeliveryAgentBin == "" {
|
||||
c.MailDeliveryAgentBin = "procmail"
|
||||
}
|
||||
if len(c.MailDeliveryAgentArgs) == 0 {
|
||||
c.MailDeliveryAgentArgs = append(c.MailDeliveryAgentArgs,
|
||||
"-d", "%user%")
|
||||
}
|
||||
|
||||
logConfig(c)
|
||||
return c, nil
|
||||
}
|
||||
@@ -57,4 +65,5 @@ func logConfig(c *Config) {
|
||||
glog.Infof(" Max data size (MB): %d", c.MaxDataSizeMb)
|
||||
glog.Infof(" Addresses: %v", c.Address)
|
||||
glog.Infof(" Monitoring address: %s", c.MonitoringAddress)
|
||||
glog.Infof(" MDA: %s %v", c.MailDeliveryAgentBin, c.MailDeliveryAgentArgs)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,12 @@ var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
type Config struct {
|
||||
// Hostname to use when we say hello.
|
||||
// For aesthetic purposes, but may help if our ip address resolves to it.
|
||||
@@ -36,8 +42,42 @@ type Config struct {
|
||||
// Address for the monitoring http server.
|
||||
// Default: no monitoring http server.
|
||||
MonitoringAddress string `protobuf:"bytes,4,opt,name=monitoring_address" json:"monitoring_address,omitempty"`
|
||||
// Mail delivery agent (MDA, also known as LDA) to use.
|
||||
// This should point to the binary to use to deliver email to local users.
|
||||
// The content of the email will be passed via stdin.
|
||||
// If it exits unsuccessfully, we assume the mail was not delivered.
|
||||
// Default: "procmail".
|
||||
MailDeliveryAgentBin string `protobuf:"bytes,5,opt,name=mail_delivery_agent_bin" json:"mail_delivery_agent_bin,omitempty"`
|
||||
// Command line arguments for the mail delivery agent. One per argument.
|
||||
// Some replacements will be done:
|
||||
// - "%user%" -> local user (anything before the @)
|
||||
// - "%domain%" -> domain (anything after the @)
|
||||
// Default: "-d", "%user" (adequate for procmail)
|
||||
MailDeliveryAgentArgs []string `protobuf:"bytes,6,rep,name=mail_delivery_agent_args" json:"mail_delivery_agent_args,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Config) Reset() { *m = Config{} }
|
||||
func (m *Config) String() string { return proto.CompactTextString(m) }
|
||||
func (*Config) ProtoMessage() {}
|
||||
func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Config)(nil), "Config")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("config.proto", fileDescriptor0) }
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 169 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x6c, 0x8e, 0x41, 0xca, 0xc2, 0x30,
|
||||
0x10, 0x46, 0xe9, 0xdf, 0xdf, 0xaa, 0x83, 0x60, 0xc9, 0xc6, 0xc1, 0x8d, 0xc5, 0x95, 0x2b, 0x37,
|
||||
0x1e, 0xc1, 0x83, 0x0c, 0x53, 0x13, 0xe3, 0x40, 0x93, 0x48, 0x12, 0x44, 0x3d, 0x8f, 0x07, 0xb5,
|
||||
0x06, 0xdc, 0xb9, 0xfd, 0xde, 0xe3, 0xf1, 0xc1, 0xe2, 0x14, 0xfc, 0x59, 0xec, 0xfe, 0x1a, 0x43,
|
||||
0x0e, 0xdb, 0x57, 0x05, 0xcd, 0xb1, 0x0c, 0xaa, 0x85, 0xd9, 0x25, 0xa4, 0xec, 0xd9, 0x19, 0xac,
|
||||
0xba, 0x6a, 0x37, 0x57, 0x08, 0xad, 0xe3, 0x3b, 0x69, 0xce, 0x4c, 0x49, 0x9e, 0x86, 0x5c, 0x8f,
|
||||
0x7f, 0x23, 0xa9, 0xd5, 0x12, 0xa6, 0xac, 0x75, 0x34, 0x29, 0x61, 0xdd, 0xd5, 0xa3, 0xba, 0x06,
|
||||
0xe5, 0x82, 0x97, 0x1c, 0xa2, 0x78, 0x4b, 0x5f, 0xf6, 0x5f, 0x32, 0x1b, 0x58, 0x39, 0x96, 0x81,
|
||||
0xb4, 0x19, 0xe4, 0x66, 0xe2, 0x83, 0xd8, 0x1a, 0x9f, 0xa9, 0x17, 0x8f, 0x93, 0x22, 0x74, 0x80,
|
||||
0xbf, 0x04, 0x8e, 0x36, 0x61, 0xf3, 0xc9, 0xf7, 0x4d, 0x79, 0x7b, 0x78, 0x07, 0x00, 0x00, 0xff,
|
||||
0xff, 0xa3, 0xe4, 0x58, 0xd3, 0xbd, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
@@ -18,5 +18,19 @@ message Config {
|
||||
// Address for the monitoring http server.
|
||||
// Default: no monitoring http server.
|
||||
string monitoring_address = 4;
|
||||
|
||||
// Mail delivery agent (MDA, also known as LDA) to use.
|
||||
// This should point to the binary to use to deliver email to local users.
|
||||
// The content of the email will be passed via stdin.
|
||||
// If it exits unsuccessfully, we assume the mail was not delivered.
|
||||
// Default: "procmail".
|
||||
string mail_delivery_agent_bin = 5;
|
||||
|
||||
// Command line arguments for the mail delivery agent. One per argument.
|
||||
// Some replacements will be done:
|
||||
// - "%user%" -> local user (anything before the @)
|
||||
// - "%domain%" -> domain (anything after the @)
|
||||
// Default: "-d", "%user" (adequate for procmail)
|
||||
repeated string mail_delivery_agent_args = 6;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,10 @@ import (
|
||||
var (
|
||||
// Location of the procmail binary, and arguments to use.
|
||||
// The string "%user%" will be replaced with the local user.
|
||||
procmailBin = "procmail"
|
||||
procmailArgs = []string{"-d", "%user%"}
|
||||
// TODO: Make these a part of the courier instance itself? Why do they
|
||||
// have to be global?
|
||||
MailDeliveryAgentBin = "procmail"
|
||||
MailDeliveryAgentArgs = []string{"-d", "%user%"}
|
||||
|
||||
// Give procmail 1m to deliver mail.
|
||||
procmailTimeout = 1 * time.Minute
|
||||
@@ -39,10 +41,10 @@ func (p *Procmail) Deliver(from string, to string, data []byte) error {
|
||||
|
||||
// Prepare the command, replacing the necessary arguments.
|
||||
args := []string{}
|
||||
for _, a := range procmailArgs {
|
||||
for _, a := range MailDeliveryAgentArgs {
|
||||
args = append(args, strings.Replace(a, "%user%", user, -1))
|
||||
}
|
||||
cmd := exec.Command(procmailBin, args...)
|
||||
cmd := exec.Command(MailDeliveryAgentBin, args...)
|
||||
|
||||
cmdStdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
|
||||
@@ -15,8 +15,8 @@ func TestProcmail(t *testing.T) {
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
procmailBin = "tee"
|
||||
procmailArgs = []string{dir + "/%user%"}
|
||||
MailDeliveryAgentBin = "tee"
|
||||
MailDeliveryAgentArgs = []string{dir + "/%user%"}
|
||||
|
||||
p := Procmail{}
|
||||
err = p.Deliver("from@x", "to@y", []byte("data"))
|
||||
@@ -31,8 +31,8 @@ func TestProcmail(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProcmailTimeout(t *testing.T) {
|
||||
procmailBin = "/bin/sleep"
|
||||
procmailArgs = []string{"1"}
|
||||
MailDeliveryAgentBin = "/bin/sleep"
|
||||
MailDeliveryAgentArgs = []string{"1"}
|
||||
procmailTimeout = 100 * time.Millisecond
|
||||
|
||||
p := Procmail{}
|
||||
@@ -48,19 +48,21 @@ func TestProcmailBadCommandLine(t *testing.T) {
|
||||
p := Procmail{}
|
||||
|
||||
// Non-existent binary.
|
||||
procmailBin = "thisdoesnotexist"
|
||||
MailDeliveryAgentBin = "thisdoesnotexist"
|
||||
err := p.Deliver("from", "to", []byte("data"))
|
||||
if err == nil {
|
||||
t.Errorf("Unexpected success: %q %v", procmailBin, procmailArgs)
|
||||
t.Errorf("Unexpected success: %q %v",
|
||||
MailDeliveryAgentBin, MailDeliveryAgentArgs)
|
||||
}
|
||||
|
||||
// Incorrect arguments.
|
||||
procmailBin = "cat"
|
||||
procmailArgs = []string{"--fail_unknown_option"}
|
||||
MailDeliveryAgentBin = "cat"
|
||||
MailDeliveryAgentArgs = []string{"--fail_unknown_option"}
|
||||
|
||||
err = p.Deliver("from", "to", []byte("data"))
|
||||
if err == nil {
|
||||
t.Errorf("Unexpected success: %q %v", procmailBin, procmailArgs)
|
||||
t.Errorf("Unexpected success: %q %v",
|
||||
MailDeliveryAgentBin, MailDeliveryAgentArgs)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +79,13 @@ var (
|
||||
InvalidUsernameErr = errors.New("username contains invalid characters")
|
||||
)
|
||||
|
||||
func New(fname string) *DB {
|
||||
return &DB{
|
||||
fname: fname,
|
||||
users: map[string]user{},
|
||||
}
|
||||
}
|
||||
|
||||
// Load the database from the given file.
|
||||
// Return the database, a list of warnings (if any), and a fatal error if the
|
||||
// database could not be loaded.
|
||||
@@ -194,7 +201,11 @@ func (db *DB) Write() error {
|
||||
base64.StdEncoding.EncodeToString([]byte(user.password)))
|
||||
}
|
||||
|
||||
return safeio.WriteFile(db.fname, buf.Bytes(), db.finfo.Mode())
|
||||
mode := os.FileMode(0660)
|
||||
if db.finfo != nil {
|
||||
mode = db.finfo.Mode()
|
||||
}
|
||||
return safeio.WriteFile(db.fname, buf.Bytes(), mode)
|
||||
}
|
||||
|
||||
// Does this user exist in the database?
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package userdb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
@@ -41,7 +42,7 @@ func dbEquals(a, b *DB) bool {
|
||||
|
||||
for k, av := range a.users {
|
||||
bv, ok := b.users[k]
|
||||
if !ok || av != bv {
|
||||
if !ok || av.name != bv.name || av.password != bv.password {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -203,6 +204,25 @@ func TestWrite(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
fname := fmt.Sprintf("%s/userdb_test-%d", os.TempDir(), os.Getpid())
|
||||
db1 := New(fname)
|
||||
db1.AddUser("user", "passwd")
|
||||
db1.Write()
|
||||
|
||||
db2, ws, err := Load(fname)
|
||||
if err != nil {
|
||||
t.Fatalf("error loading: %v", err)
|
||||
}
|
||||
if len(ws) != 0 {
|
||||
t.Errorf("warnings loading: %v", ws)
|
||||
}
|
||||
|
||||
if !dbEquals(db1, db2) {
|
||||
t.Errorf("databases differ. db1:%v != db2:%v", db1, db2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidUsername(t *testing.T) {
|
||||
fname := mustCreateDB(t, "")
|
||||
defer removeIfSuccessful(t, fname)
|
||||
|
||||
Reference in New Issue
Block a user