1
0
mirror of https://blitiri.com.ar/repos/chasquid synced 2025-12-18 14:47:03 +00:00

docs: Add missing docstrings, adjust wording to match standard style

This patch adds a missing docstrings for exported identifiers, and
adjust some of the existing ones to match the standard style.

In some cases, the identifiers were un-exported after noticing they had
no external users.

Besides improving documentation, it also reduces the linter noise
significantly.
This commit is contained in:
Alberto Bertogli
2018-03-04 15:54:34 +00:00
parent 40ae9b5f69
commit f3b01cb493
21 changed files with 154 additions and 46 deletions

View File

@@ -56,12 +56,12 @@ func main() {
}
commands := map[string]func(){
"user-add": UserAdd,
"user-remove": UserRemove,
"authenticate": Authenticate,
"check-userdb": CheckUserDB,
"aliases-resolve": AliasesResolve,
"print-config": PrintConfig,
"user-add": userAdd,
"user-remove": userRemove,
"authenticate": authenticate,
"check-userdb": checkUserDB,
"aliases-resolve": aliasesResolve,
"print-config": printConfig,
}
for cmd, f := range commands {
@@ -71,6 +71,7 @@ func main() {
}
}
// Fatalf prints the given message, then exits the program with an error code.
func Fatalf(s string, arg ...interface{}) {
fmt.Printf(s+"\n", arg...)
os.Exit(1)
@@ -109,7 +110,7 @@ func userDBFromArgs(create bool) (string, string, *userdb.DB) {
}
// chasquid-util check-userdb <domain>
func CheckUserDB() {
func checkUserDB() {
_, err := userdb.Load(userDBForDomain(""))
if err != nil {
Fatalf("Error loading database: %v", err)
@@ -119,7 +120,7 @@ func CheckUserDB() {
}
// chasquid-util user-add <username> [--password=<password>]
func UserAdd() {
func userAdd() {
user, _, db := userDBFromArgs(true)
password := getPassword()
@@ -137,7 +138,7 @@ func UserAdd() {
}
// chasquid-util authenticate <username> [--password=<password>]
func Authenticate() {
func authenticate() {
user, _, db := userDBFromArgs(false)
password := getPassword()
@@ -177,7 +178,7 @@ func getPassword() string {
}
// chasquid-util user-remove <username>
func UserRemove() {
func userRemove() {
user, _, db := userDBFromArgs(false)
present := db.RemoveUser(user)
@@ -194,7 +195,7 @@ func UserRemove() {
}
// chasquid-util aliases-resolve <address>
func AliasesResolve() {
func aliasesResolve() {
conf, err := config.Load(configDir + "/chasquid.conf")
if err != nil {
Fatalf("Error reading config")
@@ -238,7 +239,7 @@ func AliasesResolve() {
}
// chasquid-util print-config
func PrintConfig() {
func printConfig() {
conf, err := config.Load(configDir + "/chasquid.conf")
if err != nil {
Fatalf("Error reading config")

View File

@@ -72,6 +72,7 @@ type Recipient struct {
Type RType
}
// RType represents a recipient type, see the contants below for valid values.
type RType string
// Valid recipient types.
@@ -81,6 +82,8 @@ const (
)
var (
// ErrRecursionLimitExceeded is returned when the resolving lookup
// exceeded the recursion limit. Usually caused by aliases loops.
ErrRecursionLimitExceeded = fmt.Errorf("recursion limit exceeded")
// How many levels of recursions we allow during lookups.
@@ -109,6 +112,7 @@ type Resolver struct {
mu sync.Mutex
}
// NewResolver returns a new, empty Resolver.
func NewResolver() *Resolver {
return &Resolver{
files: map[string][]string{},
@@ -117,6 +121,8 @@ func NewResolver() *Resolver {
}
}
// Resolve the given address, returning the list of corresponding recipients
// (if any).
func (v *Resolver) Resolve(addr string) ([]Recipient, error) {
v.mu.Lock()
defer v.mu.Unlock()
@@ -184,14 +190,17 @@ func (v *Resolver) cleanIfLocal(addr string) string {
return user + "@" + domain
}
// AddDomain to the resolver, registering its existence.
func (v *Resolver) AddDomain(domain string) {
v.mu.Lock()
v.domains[domain] = true
v.mu.Unlock()
}
// AddAliasesFile to the resolver. The file will be parsed, and an error
// returned if it does not exist or parse correctly.
func (v *Resolver) AddAliasesFile(domain, path string) error {
// We inconditionally add the domain and file on our list.
// We unconditionally add the domain and file on our list.
// Even if the file does not exist now, it may later. This makes it be
// consider when doing Reload.
// Adding it to the domains mean that we will do drop character and suffix
@@ -219,10 +228,13 @@ func (v *Resolver) AddAliasesFile(domain, path string) error {
return nil
}
// AddAliasForTesting adds an alias to the resolver, for testing purposes.
// Not for use in production code.
func (v *Resolver) AddAliasForTesting(addr, rcpt string, rType RType) {
v.aliases[addr] = append(v.aliases[addr], Recipient{rcpt, rType})
}
// Reload aliases files for all known domains.
func (v *Resolver) Reload() error {
newAliases := map[string][]Recipient{}

View File

@@ -13,16 +13,16 @@ import (
"blitiri.com.ar/go/chasquid/internal/normalize"
)
// Interface for authentication backends.
// Backend is the common interface for all authentication backends.
type Backend interface {
Authenticate(user, password string) (bool, error)
Exists(user string) (bool, error)
Reload() error
}
// Interface for authentication backends that don't need to emit errors.
// This allows backends to avoid unnecessary complexity, in exchange for a bit
// more here.
// NoErrorBackend is the interface for authentication backends that don't need
// to emit errors. This allows backends to avoid unnecessary complexity, in
// exchange for a bit more here.
// They can be converted to normal Backend using WrapNoErrorBackend (defined
// below).
type NoErrorBackend interface {
@@ -31,6 +31,8 @@ type NoErrorBackend interface {
Reload() error
}
// Authenticator tracks the backends for each domain, and allows callers to
// query them with a more practical API.
type Authenticator struct {
// Registered backends, map of domain (string) -> Backend.
// Backend operations will _not_ include the domain in the username.
@@ -48,6 +50,7 @@ type Authenticator struct {
AuthDuration time.Duration
}
// NewAuthenticator returns a new Authenticator with no backends.
func NewAuthenticator() *Authenticator {
return &Authenticator{
backends: map[string]Backend{},
@@ -55,6 +58,7 @@ func NewAuthenticator() *Authenticator {
}
}
// Register a backend to use for the given domain.
func (a *Authenticator) Register(domain string, be Backend) {
a.backends[domain] = be
}
@@ -87,6 +91,7 @@ func (a *Authenticator) Authenticate(user, domain, password string) (bool, error
return false, nil
}
// Exists checks that user@domain exists.
func (a *Authenticator) Exists(user, domain string) (bool, error) {
if be, ok := a.backends[domain]; ok {
ok, err := be.Exists(user)

View File

@@ -81,6 +81,7 @@ func Load(path string) (*Config, error) {
return c, nil
}
// LogConfig logs the given configuration, in a human-friendly way.
func LogConfig(c *Config) {
log.Infof("Configuration:")
log.Infof(" Hostname: %q", c.Hostname)

View File

@@ -29,6 +29,8 @@ type Procmail struct {
Timeout time.Duration // Timeout for each invocation.
}
// Deliver an email. On failures, returns an error, and whether or not it is
// permanent.
func (p *Procmail) Deliver(from string, to string, data []byte) (error, bool) {
tr := trace.New("Courier.Procmail", to)
defer tr.Finish()

View File

@@ -41,6 +41,8 @@ type SMTP struct {
Dinfo *domaininfo.DB
}
// Deliver an email. On failures, returns an error, and whether or not it is
// permanent.
func (s *SMTP) Deliver(from string, to string, data []byte) (error, bool) {
a := &attempt{
courier: s,

View File

@@ -13,6 +13,7 @@ import (
// Command to generate domaininfo.pb.go.
//go:generate protoc --go_out=. domaininfo.proto
// DB represents the persistent domain information database.
type DB struct {
// Persistent store with the list of domains we know.
store *protoio.Store
@@ -23,6 +24,8 @@ type DB struct {
ev *trace.EventLog
}
// New opens a domain information database on the given dir, creating it if
// necessary. The returned database will not be loaded.
func New(dir string) (*DB, error) {
st, err := protoio.NewStore(dir)
if err != nil {

View File

@@ -21,12 +21,12 @@ import (
"unicode"
)
// Default timeout to use. We expect Dovecot to be quite fast, but don't want
// DefaultTimeout to use. We expect Dovecot to be quite fast, but don't want
// to hang forever if something gets stuck.
const DefaultTimeout = 5 * time.Second
var (
ErrUsernameNotSafe = errors.New("username not safe (contains spaces)")
errUsernameNotSafe = errors.New("username not safe (contains spaces)")
)
var defaultUserdbPaths = []string{
@@ -60,6 +60,7 @@ func NewAuth(userdb, client string) *Auth {
}
}
// String representation of this Auth, for human consumption.
func (a *Auth) String() string {
return fmt.Sprintf("DovecotAuth(%q, %q)", a.userdbAddr, a.clientAddr)
}
@@ -79,10 +80,10 @@ func (a *Auth) Check() error {
return nil
}
// Does user exist?
// Exists returns true if the user exists, false otherwise.
func (a *Auth) Exists(user string) (bool, error) {
if !isUsernameSafe(user) {
return false, ErrUsernameNotSafe
return false, errUsernameNotSafe
}
conn, err := a.dial("unix", a.userdbAddr)
@@ -126,10 +127,11 @@ func (a *Auth) Exists(user string) (bool, error) {
return false, fmt.Errorf("invalid response: %q", resp)
}
// Is the password valud for the user?
// Authenticate returns true if the password is valid for the user, false
// otherwise.
func (a *Auth) Authenticate(user, passwd string) (bool, error) {
if !isUsernameSafe(user) {
return false, ErrUsernameNotSafe
return false, errUsernameNotSafe
}
conn, err := a.dial("unix", a.clientAddr)
@@ -182,6 +184,8 @@ func (a *Auth) Authenticate(user, passwd string) (bool, error) {
return false, fmt.Errorf("invalid response: %q", resp)
}
// Reload the authenticator. It's a no-op for dovecot, but it is needed to
// conform with the auth.Backend interface.
func (a *Auth) Reload() error {
return nil
}

View File

@@ -19,12 +19,12 @@ func TestUsernameNotSafe(t *testing.T) {
"a b", " ab", "ab ", "a\tb", "a\t", " ", "\t", "\t "}
for _, c := range cases {
ok, err := a.Authenticate(c, "passwd")
if ok || err != ErrUsernameNotSafe {
if ok || err != errUsernameNotSafe {
t.Errorf("Authenticate(%q, _): got %v, %v", c, ok, err)
}
ok, err = a.Exists(c)
if ok || err != ErrUsernameNotSafe {
if ok || err != errUsernameNotSafe {
t.Errorf("Exists(%q): got %v, %v", c, ok, err)
}
}

View File

@@ -19,16 +19,19 @@ func Split(addr string) (string, string) {
return ps[0], ps[1]
}
// UserOf user@domain returns user.
func UserOf(addr string) string {
user, _ := Split(addr)
return user
}
// DomainOf user@domain returns domain.
func DomainOf(addr string) string {
_, domain := Split(addr)
return domain
}
// DomainIn checks that the domain of the address is on the given set.
func DomainIn(addr string, locals *set.String) bool {
domain := DomainOf(addr)
if domain == "" {
@@ -38,6 +41,7 @@ func DomainIn(addr string, locals *set.String) bool {
return locals.Has(domain)
}
// AddHeader adds (prepends) a MIME header to the message.
func AddHeader(data []byte, k, v string) []byte {
if len(v) > 0 {
// If the value contains newlines, indent them properly.

View File

@@ -24,20 +24,26 @@ type timedWriter struct {
w io.Writer
}
// Write the given buffer, prepending timing information.
func (t timedWriter) Write(b []byte) (int, error) {
fmt.Fprintf(t.w, "%s ", time.Now().Format("2006-01-02 15:04:05.000000"))
return t.w.Write(b)
}
// Logger contains a backend used to log data to, such as a file or syslog.
// It implements various user-friendly methods for logging mail information to
// it.
type Logger struct {
w io.Writer
once sync.Once
}
// New creates a new Logger which will write messages to the given writer.
func New(w io.Writer) *Logger {
return &Logger{w: timedWriter{w}}
}
// NewSyslog creates a new Logger which will write messages to syslog.
func NewSyslog() (*Logger, error) {
w, err := syslog.New(syslog.LOG_INFO|syslog.LOG_MAIL, "chasquid")
if err != nil {
@@ -58,10 +64,12 @@ func (l *Logger) printf(format string, args ...interface{}) {
}
}
// Listening logs that the daemon is listening on the given address.
func (l *Logger) Listening(a string) {
l.printf("daemon listening on %s\n", a)
}
// Auth logs an authentication request.
func (l *Logger) Auth(netAddr net.Addr, user string, successful bool) {
res := "succeeded"
if !successful {
@@ -72,6 +80,7 @@ func (l *Logger) Auth(netAddr net.Addr, user string, successful bool) {
authLog.Debugf(msg)
}
// Rejected logs that we've rejected an email.
func (l *Logger) Rejected(netAddr net.Addr, from string, to []string, err string) {
if from != "" {
from = fmt.Sprintf(" from=%s", from)
@@ -83,10 +92,12 @@ func (l *Logger) Rejected(netAddr net.Addr, from string, to []string, err string
l.printf("%s rejected%s%s - %v\n", netAddr, from, toStr, err)
}
// Queued logs that we have queued an email.
func (l *Logger) Queued(netAddr net.Addr, from string, to []string, id string) {
l.printf("%s from=%s queued ip=%s to=%v\n", id, from, netAddr, to)
}
// SendAttempt logs that we have attempted to send an email.
func (l *Logger) SendAttempt(id, from, to string, err error, permanent bool) {
if err == nil {
l.printf("%s from=%s to=%s sent\n", id, from, to)
@@ -99,6 +110,7 @@ func (l *Logger) SendAttempt(id, from, to string, err error, permanent bool) {
}
}
// QueueLoop logs that we have completed a queue loop.
func (l *Logger) QueueLoop(id, from string, nextDelay time.Duration) {
if nextDelay > 0 {
l.printf("%s from=%s completed loop, next in %v\n", id, from, nextDelay)
@@ -107,29 +119,35 @@ func (l *Logger) QueueLoop(id, from string, nextDelay time.Duration) {
}
}
// The default logger used in the following top-level functions.
var Default *Logger = New(ioutil.Discard)
// Default logger, used in the following top-level functions.
var Default = New(ioutil.Discard)
// Listening logs that the daemon is listening on the given address.
func Listening(a string) {
Default.Listening(a)
}
// Auth logs an authentication request.
func Auth(netAddr net.Addr, user string, successful bool) {
Default.Auth(netAddr, user, successful)
}
// Rejected logs that we've rejected an email.
func Rejected(netAddr net.Addr, from string, to []string, err string) {
Default.Rejected(netAddr, from, to, err)
}
// Queued logs that we have queued an email.
func Queued(netAddr net.Addr, from string, to []string, id string) {
Default.Queued(netAddr, from, to, id)
}
// SendAttempt logs that we have attempted to send an email.
func SendAttempt(id, from, to string, err error, permanent bool) {
Default.SendAttempt(id, from, to, err, permanent)
}
// QueueLoop logs that we have completed a queue loop.
func QueueLoop(id, from string, nextDelay time.Duration) {
Default.QueueLoop(id, from, nextDelay)
}

View File

@@ -41,7 +41,7 @@ func Domain(domain string) (string, error) {
return d, nil
}
// Name normalizes an email address, applying User and Domain to its
// Addr normalizes an email address, applying User and Domain to its
// respective components.
// On error, it will also return the original address to simplify callers.
func Addr(addr string) (string, error) {
@@ -60,8 +60,8 @@ func Addr(addr string) (string, error) {
return user + "@" + domain, nil
}
// Take an address with an ASCII domain, and convert it to Unicode as per
// IDNA, including basic normalization.
// DomainToUnicode takes an address with an ASCII domain, and convert it to
// Unicode as per IDNA, including basic normalization.
// The user part is unchanged.
func DomainToUnicode(addr string) (string, error) {
if addr == "<>" {

View File

@@ -72,10 +72,12 @@ func (s *Store) idToFname(id string) string {
return s.dir + "/" + storeIDPrefix + url.QueryEscape(id)
}
// Put a message into the store.
func (s *Store) Put(id string, m proto.Message) error {
return WriteTextMessage(s.idToFname(id), m, 0660)
}
// Get a message from the store.
func (s *Store) Get(id string, m proto.Message) (bool, error) {
err := ReadTextMessage(s.idToFname(id), m)
if os.IsNotExist(err) {
@@ -84,6 +86,7 @@ func (s *Store) Get(id string, m proto.Message) (bool, error) {
return err == nil, err
}
// ListIDs in the store.
func (s *Store) ListIDs() ([]string, error) {
ids := []string{}

View File

@@ -146,6 +146,7 @@ func (q *Queue) Load() error {
return nil
}
// Len returns the number of elements in the queue.
func (q *Queue) Len() int {
q.mu.RLock()
defer q.mu.RUnlock()
@@ -253,7 +254,7 @@ func (q *Queue) DumpString() string {
return s
}
// An item in the queue.
// An Item in the queue.
type Item struct {
// Base the item on the protobuf message.
// We will use this for serialization, so any fields below are NOT
@@ -267,6 +268,7 @@ type Item struct {
CreatedAt time.Time
}
// ItemFromFile loads an item from the given file.
func ItemFromFile(fname string) (*Item, error) {
item := &Item{}
err := protoio.ReadTextMessage(fname, &item.Message)
@@ -278,6 +280,7 @@ func ItemFromFile(fname string) (*Item, error) {
return item, err
}
// WriteTo saves an item to the given directory.
func (item *Item) WriteTo(dir string) error {
item.Lock()
defer item.Unlock()
@@ -294,6 +297,7 @@ func (item *Item) WriteTo(dir string) error {
return protoio.WriteTextMessage(path, &item.Message, 0600)
}
// SendLoop repeatedly attempts to send the item.
func (item *Item) SendLoop(q *Queue) {
tr := trace.New("Queue.SendLoop", item.ID)
defer tr.Finish()

View File

@@ -9,7 +9,7 @@ import (
"syscall"
)
// Type FileOp represents an operation on a file (passed by its name).
// FileOp represents an operation on a file (passed by its name).
type FileOp func(fname string) error
// WriteFile writes data to a file named by filename, atomically.

View File

@@ -1,16 +1,19 @@
// Package set implement sets for various types. Well, only string for now :)
package set
// String set.
type String struct {
m map[string]struct{}
}
// NewString returns a new string set, with the given values in it.
func NewString(values ...string) *String {
s := &String{}
s.Add(values...)
return s
}
// Add values to the string set.
func (s *String) Add(values ...string) {
if s.m == nil {
s.m = map[string]struct{}{}
@@ -21,6 +24,7 @@ func (s *String) Add(values ...string) {
}
}
// Has checks if the set has the given value.
func (s *String) Has(value string) bool {
// We explicitly allow s to be nil *in this function* to simplify callers'
// code. Note that Add will not tolerate it, and will panic.

View File

@@ -22,6 +22,7 @@ type Client struct {
*smtp.Client
}
// NewClient uses the given connection to create a new Client.
func NewClient(conn net.Conn, host string) (*Client, error) {
c, err := smtp.NewClient(conn, host)
if err != nil {
@@ -129,7 +130,7 @@ func isASCII(s string) bool {
return true
}
// ErrIsPermanent returns true if the error is permanent, and false otherwise.
// IsPermanent returns true if the error is permanent, and false otherwise.
// If it can't tell, it returns false.
func IsPermanent(err error) bool {
terr, ok := err.(*textproto.Error)

View File

@@ -52,7 +52,7 @@ var (
disableSPFForTesting = false
)
// Mode for a socket (listening or connection).
// SocketMode represents the mode for a socket (listening or connection).
// We keep them distinct, as policies can differ between them.
type SocketMode struct {
// Is this mode submission?
@@ -81,7 +81,7 @@ var (
ModeSubmissionTLS = SocketMode{IsSubmission: true, TLS: true}
)
// Incoming SMTP connection.
// Conn represents an incoming SMTP connection.
type Conn struct {
// Main hostname, used for display only.
hostname string
@@ -146,10 +146,13 @@ type Conn struct {
commandTimeout time.Duration
}
// Close the connection.
func (c *Conn) Close() {
c.conn.Close()
}
// Handle implements the main protocol loop (reading commands, sending
// replies).
func (c *Conn) Handle() {
defer c.Close()
@@ -265,6 +268,7 @@ loop:
}
}
// HELO SMTP command handler.
func (c *Conn) HELO(params string) (code int, msg string) {
if len(strings.TrimSpace(params)) == 0 {
return 501, "Invisible customers are not welcome!"
@@ -282,6 +286,7 @@ func (c *Conn) HELO(params string) (code int, msg string) {
return 250, msg
}
// EHLO SMTP command handler.
func (c *Conn) EHLO(params string) (code int, msg string) {
if len(strings.TrimSpace(params)) == 0 {
return 501, "Invisible customers are not welcome!"
@@ -303,10 +308,12 @@ func (c *Conn) EHLO(params string) (code int, msg string) {
return 250, buf.String()
}
// HELP SMTP command handler.
func (c *Conn) HELP(params string) (code int, msg string) {
return 214, "hoy por ti, mañana por mi"
}
// RSET SMTP command handler.
func (c *Conn) RSET(params string) (code int, msg string) {
c.resetEnvelope()
@@ -319,6 +326,7 @@ func (c *Conn) RSET(params string) (code int, msg string) {
return 250, msgs[rand.Int()%len(msgs)]
}
// VRFY SMTP command handler.
func (c *Conn) VRFY(params string) (code int, msg string) {
// 252 can be used for cases like ours, when we don't really want to
// confirm or deny anything.
@@ -326,6 +334,7 @@ func (c *Conn) VRFY(params string) (code int, msg string) {
return 252, "You have a strange feeling for a moment, then it passes."
}
// EXPN SMTP command handler.
func (c *Conn) EXPN(params string) (code int, msg string) {
// 252 can be used for cases like ours, when we don't really want to
// confirm or deny anything.
@@ -333,10 +342,12 @@ func (c *Conn) EXPN(params string) (code int, msg string) {
return 252, "You feel disoriented for a moment."
}
// NOOP SMTP command handler.
func (c *Conn) NOOP(params string) (code int, msg string) {
return 250, "You hear a faint typing noise."
}
// MAIL SMTP command handler.
func (c *Conn) MAIL(params string) (code int, msg string) {
// params should be: "FROM:<name@host>", and possibly followed by
// options such as "BODY=8BITMIME" (which we ignore).
@@ -466,6 +477,7 @@ func (c *Conn) secLevelCheck(addr string) bool {
return ok
}
// RCPT SMTP command handler.
func (c *Conn) RCPT(params string) (code int, msg string) {
// params should be: "TO:<name@host>", and possibly followed by options
// such as "NOTIFY=SUCCESS,DELAY" (which we ignore).
@@ -531,6 +543,7 @@ func (c *Conn) RCPT(params string) (code int, msg string) {
return 250, "You have an eerie feeling..."
}
// DATA SMTP command handler.
func (c *Conn) DATA(params string) (code int, msg string) {
if c.ehloAddress == "" {
return 503, "Invisible customers are not welcome!"
@@ -796,6 +809,7 @@ func boolToStr(b bool) string {
return "0"
}
// STARTTLS SMTP command handler.
func (c *Conn) STARTTLS(params string) (code int, msg string) {
if c.onTLS {
return 503, "You are already wearing that!"
@@ -840,6 +854,7 @@ func (c *Conn) STARTTLS(params string) (code int, msg string) {
return 0, ""
}
// AUTH SMTP command handler.
func (c *Conn) AUTH(params string) (code int, msg string) {
if !c.onTLS {
return 503, "You feel vulnerable"

View File

@@ -28,6 +28,7 @@ var (
"how often to reload, ONLY FOR TESTING")
)
// Server represents an SMTP server instance.
type Server struct {
// Main hostname, used for display only.
Hostname string
@@ -70,6 +71,7 @@ type Server struct {
PostDataHook string
}
// NewServer returns a new empty Server.
func NewServer() *Server {
return &Server{
addrs: map[SocketMode][]string{},
@@ -83,6 +85,7 @@ func NewServer() *Server {
}
}
// AddCerts (TLS) to the server.
func (s *Server) AddCerts(certPath, keyPath string) error {
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
if err != nil {
@@ -92,36 +95,44 @@ func (s *Server) AddCerts(certPath, keyPath string) error {
return nil
}
// AddAddr adds an address for the server to listen on.
func (s *Server) AddAddr(a string, m SocketMode) {
s.addrs[m] = append(s.addrs[m], a)
}
// AddListeners adds listeners for the server to listen on.
func (s *Server) AddListeners(ls []net.Listener, m SocketMode) {
s.listeners[m] = append(s.listeners[m], ls...)
}
// AddDomain adds a local domain to the server.
func (s *Server) AddDomain(d string) {
s.localDomains.Add(d)
s.aliasesR.AddDomain(d)
}
// AddUserDB adds a userdb.DB instance as backend for the domain.
func (s *Server) AddUserDB(domain string, db *userdb.DB) {
s.authr.Register(domain, auth.WrapNoErrorBackend(db))
}
// AddAliasesFile adds an aliases file for the given domain.
func (s *Server) AddAliasesFile(domain, f string) error {
return s.aliasesR.AddAliasesFile(domain, f)
}
// SetAuthFallback sets the authentication backend to use as fallback.
func (s *Server) SetAuthFallback(be auth.Backend) {
s.authr.Fallback = be
}
// SetAliasesConfig sets the aliases configuration options.
func (s *Server) SetAliasesConfig(suffixSep, dropChars string) {
s.aliasesR.SuffixSep = suffixSep
s.aliasesR.DropChars = dropChars
}
// InitDomainInfo initializes the domain info database.
func (s *Server) InitDomainInfo(dir string) *domaininfo.DB {
var err error
s.dinfo, err = domaininfo.New(dir)
@@ -137,6 +148,7 @@ func (s *Server) InitDomainInfo(dir string) *domaininfo.DB {
return s.dinfo
}
// InitQueue initializes the queue.
func (s *Server) InitQueue(path string, localC, remoteC courier.Courier) {
q := queue.New(path, s.localDomains, s.aliasesR, localC, remoteC)
err := q.Load()
@@ -151,7 +163,7 @@ func (s *Server) InitQueue(path string, localC, remoteC courier.Courier) {
})
}
// PeriodicallyReload some of the server's information, such as aliases and
// periodicallyReload some of the server's information, such as aliases and
// the user databases.
func (s *Server) periodicallyReload() {
for range time.Tick(*reloadEvery) {
@@ -167,6 +179,8 @@ func (s *Server) periodicallyReload() {
}
}
// ListenAndServe on the addresses and listeners that were previously added.
// This function will not return.
func (s *Server) ListenAndServe() {
if len(s.tlsConfig.Certificates) == 0 {
// chasquid assumes there's at least one valid certificate (for things

View File

@@ -10,12 +10,14 @@ import (
nettrace "golang.org/x/net/trace"
)
// A Trace represents an active request.
type Trace struct {
family string
title string
t nettrace.Trace
}
// New trace.
func New(family, title string) *Trace {
t := &Trace{family, title, nettrace.New(family, title)}
@@ -26,6 +28,7 @@ func New(family, title string) *Trace {
return t
}
// Printf adds this message to the trace's log.
func (t *Trace) Printf(format string, a ...interface{}) {
t.t.LazyPrintf(format, a...)
@@ -33,6 +36,7 @@ func (t *Trace) Printf(format string, a ...interface{}) {
quote(fmt.Sprintf(format, a...)))
}
// Debugf adds this message to the trace's log, with a debugging level.
func (t *Trace) Debugf(format string, a ...interface{}) {
t.t.LazyPrintf(format, a...)
@@ -40,6 +44,7 @@ func (t *Trace) Debugf(format string, a ...interface{}) {
t.family, t.title, quote(fmt.Sprintf(format, a...)))
}
// Errorf adds this message to the trace's log, with an error level.
func (t *Trace) Errorf(format string, a ...interface{}) error {
// Note we can't just call t.Error here, as it breaks caller logging.
err := fmt.Errorf(format, a...)
@@ -51,6 +56,8 @@ func (t *Trace) Errorf(format string, a ...interface{}) error {
return err
}
// Error marks the trace as having seen an error, and also logs it to the
// trace's log.
func (t *Trace) Error(err error) error {
t.t.SetError()
t.t.LazyPrintf("error: %v", err)
@@ -61,20 +68,24 @@ func (t *Trace) Error(err error) error {
return err
}
// Finish the trace. It should not be changed after this is called.
func (t *Trace) Finish() {
t.t.Finish()
}
// EventLog is used for tracing long-lived objects.
type EventLog struct {
family string
title string
e nettrace.EventLog
}
// NewEventLog returns a new EventLog.
func NewEventLog(family, title string) *EventLog {
return &EventLog{family, title, nettrace.NewEventLog(family, title)}
}
// Printf adds the message to the EventLog.
func (e *EventLog) Printf(format string, a ...interface{}) {
e.e.Printf(format, a...)
@@ -82,6 +93,7 @@ func (e *EventLog) Printf(format string, a ...interface{}) {
quote(fmt.Sprintf(format, a...)))
}
// Debugf adds the message to the EventLog, with a debugging level.
func (e *EventLog) Debugf(format string, a ...interface{}) {
e.e.Printf(format, a...)
@@ -89,6 +101,7 @@ func (e *EventLog) Debugf(format string, a ...interface{}) {
quote(fmt.Sprintf(format, a...)))
}
// Errorf adds the message to the EventLog, with an error level.
func (e *EventLog) Errorf(format string, a ...interface{}) error {
err := fmt.Errorf(format, a...)
e.e.Errorf("error: %v", err)

View File

@@ -45,6 +45,7 @@ import (
"blitiri.com.ar/go/chasquid/internal/protoio"
)
// DB represents a single user database.
type DB struct {
fname string
db *ProtoDB
@@ -53,10 +54,7 @@ type DB struct {
mu sync.RWMutex
}
var (
ErrInvalidUsername = errors.New("invalid username")
)
// New returns a new user database, on the given file name.
func New(fname string) *DB {
return &DB{
fname: fname,
@@ -106,7 +104,8 @@ func (db *DB) Write() error {
return protoio.WriteTextMessage(db.fname, db.db, 0660)
}
// Is this password valid for the user?
// Authenticate returns true if the password is valid for the user, false
// otherwise.
func (db *DB) Authenticate(name, plainPassword string) bool {
db.mu.RLock()
passwd, ok := db.db.Users[name]
@@ -119,6 +118,7 @@ func (db *DB) Authenticate(name, plainPassword string) bool {
return passwd.PasswordMatches(plainPassword)
}
// PasswordMatches returns true if the given password is a match.
func (p *Password) PasswordMatches(plain string) bool {
switch s := p.Scheme.(type) {
case nil:
@@ -132,11 +132,11 @@ func (p *Password) PasswordMatches(plain string) bool {
}
}
// Add a user to the database. If the user is already present, override it.
// AddUser to the database. If the user is already present, override it.
// Note we enforce that the name has been normalized previously.
func (db *DB) AddUser(name, plainPassword string) error {
if norm, err := normalize.User(name); err != nil || name != norm {
return ErrInvalidUsername
return errors.New("invalid username")
}
s := &Scrypt{
@@ -178,7 +178,7 @@ func (db *DB) RemoveUser(name string) bool {
return present
}
// Exists returns true if the user is present, False otherwise.
// Exists returns true if the user is present, false otherwise.
func (db *DB) Exists(name string) bool {
db.mu.Lock()
_, present := db.db.Users[name]
@@ -190,7 +190,8 @@ func (db *DB) Exists(name string) bool {
// Encryption schemes
//
// Plain text scheme. Useful mostly for testing and debugging.
// PasswordMatches implementation for the plain text scheme.
// Useful mostly for testing and debugging.
// TODO: Do we really need this? Removing it would make accidents less likely
// to happen. Consider doing so when we add another scheme, so we a least have
// two and multi-scheme support does not bit-rot.
@@ -198,7 +199,8 @@ func (p *Plain) PasswordMatches(plain string) bool {
return plain == string(p.Password)
}
// scrypt scheme, which we use by default.
// PasswordMatches implementation for the scrypt scheme, which we use by
// default.
func (s *Scrypt) PasswordMatches(plain string) bool {
dk, err := scrypt.Key([]byte(plain), s.Salt,
1<<s.LogN, int(s.R), int(s.P), int(s.KeyLen))