From 281d5a4a9dcc7de6ede63b84952ad70f7596938a Mon Sep 17 00:00:00 2001 From: teru Date: Tue, 23 Aug 2016 17:37:20 +0900 Subject: [PATCH] Implement disable/enable a Domain #8 --- const.go | 1 + database.go | 24 ++++++++++++++ domain.go | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++ repository.go | 7 ++-- 4 files changed, 120 insertions(+), 3 deletions(-) diff --git a/const.go b/const.go index 2cf9aa6..b768edd 100644 --- a/const.go +++ b/const.go @@ -5,6 +5,7 @@ const ( DirNameConfig = ".mailfull" FileNameConfig = "config" + FileNameDomainDisable = ".vdomaindisable" FileNameAliasDomains = ".valiasdomains" FileNameUsersPassword = ".vpasswd" FileNameUserForwards = ".forward" diff --git a/database.go b/database.go index 6cc5ad8..e12cf5c 100644 --- a/database.go +++ b/database.go @@ -70,6 +70,10 @@ func (r *Repository) generateDbDomains(md *MailData) error { defer dbDomains.Close() for _, domain := range md.Domains { + if domain.Disabled() { + continue + } + if _, err := fmt.Fprintf(dbDomains, "%s virtual\n", domain.Name()); err != nil { return err } @@ -95,6 +99,10 @@ func (r *Repository) generateDbDestinations(md *MailData) error { defer dbDestinations.Close() for _, domain := range md.Domains { + if domain.Disabled() { + continue + } + // ho-ge.example.com -> ho_ge.example.com underscoredDomainName := domain.Name() underscoredDomainName = strings.Replace(underscoredDomainName, `-`, `_`, -1) @@ -153,6 +161,10 @@ func (r *Repository) generateDbMaildirs(md *MailData) error { defer dbMaildirs.Close() for _, domain := range md.Domains { + if domain.Disabled() { + continue + } + for _, user := range domain.Users { if _, err := fmt.Fprintf(dbMaildirs, "%s@%s %s/%s/Maildir/\n", user.Name(), domain.Name(), domain.Name(), user.Name()); err != nil { return err @@ -174,6 +186,10 @@ func (r *Repository) generateDbLocaltable(md *MailData) error { defer dbLocaltable.Close() for _, domain := range md.Domains { + if domain.Disabled() { + continue + } + // ho-ge.example.com -> ho_ge\.example\.com escapedDomainName := domain.Name() escapedDomainName = strings.Replace(escapedDomainName, `-`, `_`, -1) @@ -198,6 +214,10 @@ func (r *Repository) generateDbForwards(md *MailData) error { defer dbForwards.Close() for _, domain := range md.Domains { + if domain.Disabled() { + continue + } + // ho-ge.example.com -> ho_ge.example.com underscoredDomainName := domain.Name() underscoredDomainName = strings.Replace(underscoredDomainName, `-`, `_`, -1) @@ -234,6 +254,10 @@ func (r *Repository) generateDbPasswords(md *MailData) error { defer dbPasswords.Close() for _, domain := range md.Domains { + if domain.Disabled() { + continue + } + for _, user := range domain.Users { if _, err := fmt.Fprintf(dbPasswords, "%s@%s:%s\n", user.Name(), domain.Name(), user.HashedPassword()); err != nil { return err diff --git a/domain.go b/domain.go index c3eb9b8..c0089d7 100644 --- a/domain.go +++ b/domain.go @@ -82,6 +82,12 @@ func (r *Repository) Domains() ([]*Domain, error) { continue } + disabled, err := r.domainDisabled(name) + if err != nil { + return nil, err + } + domain.SetDisabled(disabled) + domains = append(domains, domain) } @@ -114,9 +120,38 @@ func (r *Repository) Domain(domainName string) (*Domain, error) { return nil, err } + disabled, err := r.domainDisabled(name) + if err != nil { + return nil, err + } + domain.SetDisabled(disabled) + return domain, nil } +// domainDisabled returns true if the input Domain is disabled. +func (r *Repository) domainDisabled(domainName string) (bool, error) { + if !validDomainName(domainName) { + return false, ErrInvalidDomainName + } + + fi, err := os.Stat(filepath.Join(r.DirMailDataPath, domainName, FileNameDomainDisable)) + + if err != nil { + if err.(*os.PathError).Err == syscall.ENOENT { + return false, nil + } + + return false, err + } + + if fi.IsDir() { + return false, ErrInvalidFormatDomainDisabled + } + + return true, nil +} + // DomainCreate creates the input Domain. func (r *Repository) DomainCreate(domain *Domain) error { existDomain, err := r.Domain(domain.Name()) @@ -170,6 +205,29 @@ func (r *Repository) DomainCreate(domain *Domain) error { } catchAllUserFile.Close() + if domain.Disabled() { + if err := r.writeDomainDisabledFile(domain.Name(), domain.Disabled()); err != nil { + return err + } + } + + return nil +} + +// DomainUpdate updates the input Domain. +func (r *Repository) DomainUpdate(domain *Domain) error { + existDomain, err := r.Domain(domain.Name()) + if err != nil { + return err + } + if existDomain == nil { + return ErrDomainNotExist + } + + if err := r.writeDomainDisabledFile(domain.Name(), domain.Disabled()); err != nil { + return err + } + return nil } @@ -202,3 +260,36 @@ func (r *Repository) DomainRemove(domainName string) error { return nil } + +// writeDomainDisabledFile creates/removes the disabled file. +func (r *Repository) writeDomainDisabledFile(domainName string, disabled bool) error { + if !validDomainName(domainName) { + return ErrInvalidDomainName + } + + nowDisabled, err := r.domainDisabled(domainName) + if err != nil { + return err + } + + domainDisabledFileName := filepath.Join(r.DirMailDataPath, domainName, FileNameDomainDisable) + + if !nowDisabled && disabled { + file, err := os.OpenFile(domainDisabledFileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + return err + } + if err := file.Chown(r.uid, r.gid); err != nil { + return err + } + file.Close() + } + + if nowDisabled && !disabled { + if err := os.Remove(domainDisabledFileName); err != nil { + return err + } + } + + return nil +} diff --git a/repository.go b/repository.go index 187c997..f2f3717 100644 --- a/repository.go +++ b/repository.go @@ -34,9 +34,10 @@ var ( ErrAliasUserNotExist = errors.New("AliasUser: not exist") ErrAliasUserAlreadyExist = errors.New("AliasUser: already exist") - ErrInvalidFormatUsersPassword = errors.New("User: password file invalid format") - ErrInvalidFormatAliasDomain = errors.New("AliasDomain: file invalid format") - ErrInvalidFormatAliasUsers = errors.New("AliasUsers: file invalid format") + ErrInvalidFormatDomainDisabled = errors.New("Domain: disabled file invalid format") + ErrInvalidFormatUsersPassword = errors.New("User: password file invalid format") + ErrInvalidFormatAliasDomain = errors.New("AliasDomain: file invalid format") + ErrInvalidFormatAliasUsers = errors.New("AliasUsers: file invalid format") ) // RepositoryConfig is used to configure a Repository.