mirror of
https://github.com/directorz/mailfull-go.git
synced 2025-12-20 19:17:02 +00:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0118652074 | ||
|
|
bf4b63556a | ||
|
|
24ee3c26b0 | ||
|
|
49dd05f3bf | ||
|
|
009ee84e63 | ||
|
|
281d5a4a9d | ||
|
|
c2dca41057 | ||
|
|
4f99f11732 | ||
|
|
a3d3684fda | ||
|
|
c183bba7aa | ||
|
|
b52a7cde7f | ||
|
|
0926ffd318 | ||
|
|
c9201c8fc1 | ||
|
|
fe16bb86f9 | ||
|
|
bd6228197a | ||
|
|
1329747f54 | ||
|
|
ab3c3315f9 | ||
|
|
a76596c348 |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1 +1,6 @@
|
||||
/cli/mailfull/mailfull
|
||||
/vendor
|
||||
/build
|
||||
/release
|
||||
/github_token
|
||||
|
||||
/cmd/mailfull/mailfull
|
||||
|
||||
6
Gomfile
Normal file
6
Gomfile
Normal file
@@ -0,0 +1,6 @@
|
||||
gom 'github.com/BurntSushi/toml'
|
||||
gom 'github.com/armon/go-radix'
|
||||
gom 'github.com/bgentry/speakeasy'
|
||||
gom 'github.com/jsimonetti/pwscheme'
|
||||
gom 'github.com/mattn/go-isatty'
|
||||
gom 'github.com/mitchellh/cli'
|
||||
53
Makefile
Normal file
53
Makefile
Normal file
@@ -0,0 +1,53 @@
|
||||
GOVERSION=$(shell go version)
|
||||
GOOS=$(word 1,$(subst /, ,$(lastword $(GOVERSION))))
|
||||
GOARCH=$(word 2,$(subst /, ,$(lastword $(GOVERSION))))
|
||||
DIR_BUILD=build
|
||||
DIR_RELEASE=release
|
||||
VERSION=$(patsubst "%",%,$(lastword $(shell grep 'const Version' version.go)))
|
||||
|
||||
.PHONY: build build-linux-amd64 build-linux-386 clean
|
||||
|
||||
default: build
|
||||
|
||||
installdeps:
|
||||
gom install
|
||||
|
||||
build:
|
||||
go build -v -ldflags "-X main.gittag=`git rev-parse --short HEAD`" -o build/mailfull_$(GOOS)_$(GOARCH)/mailfull cmd/mailfull/mailfull.go
|
||||
|
||||
build-linux-amd64:
|
||||
docker run --rm -v $(PWD):/go/src/github.com/directorz/mailfull-go -w /go/src/github.com/directorz/mailfull-go \
|
||||
-e GOOS=linux -e GOARCH=amd64 golang:1.7 \
|
||||
go build -v -ldflags "-X main.gittag=`git rev-parse --short HEAD`" -o "build/mailfull_linux_amd64/mailfull" cmd/mailfull/mailfull.go
|
||||
|
||||
build-linux-386:
|
||||
docker run --rm -v $(PWD):/go/src/github.com/directorz/mailfull-go -w /go/src/github.com/directorz/mailfull-go \
|
||||
-e GOOS=linux -e GOARCH=386 golang:1.7 \
|
||||
go build -v -ldflags "-X main.gittag=`git rev-parse --short HEAD`" -o "build/mailfull_linux_386/mailfull" cmd/mailfull/mailfull.go
|
||||
|
||||
release: release-linux-amd64 release-linux-386
|
||||
|
||||
release-linux-amd64: build-linux-amd64
|
||||
@$(MAKE) release-doc release-targz GOOS=linux GOARCH=amd64
|
||||
|
||||
release-linux-386: build-linux-386
|
||||
@$(MAKE) release-doc release-targz GOOS=linux GOARCH=386
|
||||
|
||||
release-doc:
|
||||
cp -a README.md doc $(DIR_BUILD)/mailfull_$(GOOS)_$(GOARCH)
|
||||
|
||||
release-targz: dir-$(DIR_RELEASE)
|
||||
tar zcfp $(DIR_RELEASE)/mailfull_$(GOOS)_$(GOARCH).tar.gz -C $(DIR_BUILD) mailfull_$(GOOS)_$(GOARCH)
|
||||
|
||||
dir-$(DIR_RELEASE):
|
||||
mkdir -p $(DIR_RELEASE)
|
||||
|
||||
release-upload: release-linux-amd64 release-linux-386 release-github-token
|
||||
ghr -u directorz -r mailfull-go -t $(shell cat github_token) --replace --draft $(VERSION) $(DIR_RELEASE)
|
||||
|
||||
release-github-token: github_token
|
||||
@echo "file \"github_token\" is required"
|
||||
|
||||
clean:
|
||||
-rm -rf $(DIR_BUILD)
|
||||
-rm -rf $(DIR_RELEASE)
|
||||
11
README.md
11
README.md
@@ -16,6 +16,11 @@ Features
|
||||
Installation
|
||||
------------
|
||||
|
||||
### Binary
|
||||
|
||||
You can download archive file from [releases page](https://github.com/directorz/mailfull-go/releases) .
|
||||
Download and unpack the archive file, and put the binary file to somewhere you want.
|
||||
|
||||
### go get
|
||||
|
||||
Installed in `$GOPATH/bin`
|
||||
@@ -39,6 +44,7 @@ Initialize a directory as a Mailfull repository.
|
||||
```
|
||||
$ mkdir /path/to/repo && cd /path/to/repo
|
||||
$ mailfull init
|
||||
$ mailfull commit
|
||||
```
|
||||
|
||||
Generate configurations for Postfix and Dovecot. (Edit as needed.)
|
||||
@@ -66,3 +72,8 @@ Add a new domain and user.
|
||||
```
|
||||
|
||||
Enjoy!
|
||||
|
||||
More info
|
||||
---------
|
||||
|
||||
See [documentation](doc/README.md)
|
||||
|
||||
83
cmd/mailfull/command/domaindisable.go
Normal file
83
cmd/mailfull/command/domaindisable.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/directorz/mailfull-go"
|
||||
)
|
||||
|
||||
// DomainDisableCommand represents a DomainDisableCommand.
|
||||
type DomainDisableCommand struct {
|
||||
Meta
|
||||
}
|
||||
|
||||
// Synopsis returns a one-line synopsis.
|
||||
func (c *DomainDisableCommand) Synopsis() string {
|
||||
return "Disable a domain temporarily."
|
||||
}
|
||||
|
||||
// Help returns long-form help text.
|
||||
func (c *DomainDisableCommand) Help() string {
|
||||
txt := fmt.Sprintf(`
|
||||
Usage:
|
||||
%s %s domain
|
||||
|
||||
Description:
|
||||
%s
|
||||
|
||||
Required Args:
|
||||
domain
|
||||
The domain name that you want to disable.
|
||||
`,
|
||||
c.CmdName, c.SubCmdName,
|
||||
c.Synopsis())
|
||||
|
||||
return txt[1:]
|
||||
}
|
||||
|
||||
// Run runs the command and returns the exit status.
|
||||
func (c *DomainDisableCommand) Run(args []string) int {
|
||||
if len(args) != 1 {
|
||||
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
|
||||
return 1
|
||||
}
|
||||
|
||||
domainName := args[0]
|
||||
|
||||
repo, err := mailfull.OpenRepository(".")
|
||||
if err != nil {
|
||||
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
domain, err := repo.Domain(domainName)
|
||||
if err != nil {
|
||||
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
|
||||
return 1
|
||||
}
|
||||
if domain == nil {
|
||||
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", mailfull.ErrDomainNotExist)
|
||||
return 1
|
||||
}
|
||||
|
||||
domain.SetDisabled(true)
|
||||
|
||||
if err := repo.DomainUpdate(domain); err != nil {
|
||||
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
mailData, err := repo.MailData()
|
||||
if err != nil {
|
||||
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
err = repo.GenerateDatabases(mailData)
|
||||
if err != nil {
|
||||
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
83
cmd/mailfull/command/domainenable.go
Normal file
83
cmd/mailfull/command/domainenable.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/directorz/mailfull-go"
|
||||
)
|
||||
|
||||
// DomainEnableCommand represents a DomainEnableCommand.
|
||||
type DomainEnableCommand struct {
|
||||
Meta
|
||||
}
|
||||
|
||||
// Synopsis returns a one-line synopsis.
|
||||
func (c *DomainEnableCommand) Synopsis() string {
|
||||
return "Enable a domain."
|
||||
}
|
||||
|
||||
// Help returns long-form help text.
|
||||
func (c *DomainEnableCommand) Help() string {
|
||||
txt := fmt.Sprintf(`
|
||||
Usage:
|
||||
%s %s domain
|
||||
|
||||
Description:
|
||||
%s
|
||||
|
||||
Required Args:
|
||||
domain
|
||||
The domain name that you want to enable.
|
||||
`,
|
||||
c.CmdName, c.SubCmdName,
|
||||
c.Synopsis())
|
||||
|
||||
return txt[1:]
|
||||
}
|
||||
|
||||
// Run runs the command and returns the exit status.
|
||||
func (c *DomainEnableCommand) Run(args []string) int {
|
||||
if len(args) != 1 {
|
||||
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
|
||||
return 1
|
||||
}
|
||||
|
||||
domainName := args[0]
|
||||
|
||||
repo, err := mailfull.OpenRepository(".")
|
||||
if err != nil {
|
||||
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
domain, err := repo.Domain(domainName)
|
||||
if err != nil {
|
||||
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
|
||||
return 1
|
||||
}
|
||||
if domain == nil {
|
||||
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", mailfull.ErrDomainNotExist)
|
||||
return 1
|
||||
}
|
||||
|
||||
domain.SetDisabled(false)
|
||||
|
||||
if err := repo.DomainUpdate(domain); err != nil {
|
||||
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
mailData, err := repo.MailData()
|
||||
if err != nil {
|
||||
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
err = repo.GenerateDatabases(mailData)
|
||||
if err != nil {
|
||||
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -25,6 +25,7 @@ Usage:
|
||||
|
||||
Description:
|
||||
%s
|
||||
Disabled domains are marked "!" the beginning.
|
||||
`,
|
||||
c.CmdName, c.SubCmdName,
|
||||
c.Synopsis())
|
||||
@@ -48,7 +49,12 @@ func (c *DomainsCommand) Run(args []string) int {
|
||||
sort.Sort(mailfull.DomainSlice(domains))
|
||||
|
||||
for _, domain := range domains {
|
||||
fmt.Fprintf(c.UI.Writer, "%s\n", domain.Name())
|
||||
disableStr := ""
|
||||
if domain.Disabled() {
|
||||
disableStr = "!"
|
||||
}
|
||||
|
||||
fmt.Fprintf(c.UI.Writer, "%s%s\n", disableStr, domain.Name())
|
||||
}
|
||||
|
||||
return 0
|
||||
|
||||
@@ -81,9 +81,9 @@ func (c *UserCheckPwCommand) Run(args []string) int {
|
||||
}
|
||||
|
||||
if len(args) != 2 {
|
||||
input, err1 := c.UI.AskSecret(fmt.Sprintf("Enter password for %s:", address))
|
||||
if err1 != nil {
|
||||
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err1)
|
||||
input, err := c.UI.AskSecret(fmt.Sprintf("Enter password for %s:", address))
|
||||
if err != nil {
|
||||
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,11 @@ func (c *UserDelCommand) Run(args []string) int {
|
||||
return 1
|
||||
}
|
||||
|
||||
if userName == "postmaster" {
|
||||
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] Cannot delete postmaster.\n")
|
||||
return 1
|
||||
}
|
||||
|
||||
if err := repo.UserRemove(domainName, userName); err != nil {
|
||||
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
|
||||
return 1
|
||||
|
||||
@@ -81,14 +81,14 @@ func (c *UserPasswdCommand) Run(args []string) int {
|
||||
}
|
||||
|
||||
if len(args) != 2 {
|
||||
input1, err1 := c.UI.AskSecret(fmt.Sprintf("Enter new password for %s:", address))
|
||||
if err1 != nil {
|
||||
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err1)
|
||||
input1, err := c.UI.AskSecret(fmt.Sprintf("Enter new password for %s:", address))
|
||||
if err != nil {
|
||||
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
|
||||
return 1
|
||||
}
|
||||
input2, err2 := c.UI.AskSecret("Retype new password:")
|
||||
if err2 != nil {
|
||||
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err2)
|
||||
input2, err := c.UI.AskSecret("Retype new password:")
|
||||
if err != nil {
|
||||
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
|
||||
return 1
|
||||
}
|
||||
if input1 != input2 {
|
||||
@@ -100,9 +100,9 @@ func (c *UserPasswdCommand) Run(args []string) int {
|
||||
|
||||
hashedPassword := mailfull.NeverMatchHashedPassword
|
||||
if rawPassword != "" {
|
||||
str, errHash := ssha.Generate(rawPassword, 4)
|
||||
if errHash != nil {
|
||||
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", errHash)
|
||||
str, err := ssha.Generate(rawPassword, 4)
|
||||
if err != nil {
|
||||
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
|
||||
return 1
|
||||
}
|
||||
hashedPassword = str
|
||||
|
||||
@@ -62,6 +62,14 @@ func main() {
|
||||
meta.SubCmdName = c.Subcommand()
|
||||
return &command.DomainDelCommand{Meta: meta}, nil
|
||||
},
|
||||
"domaindisable": func() (cli.Command, error) {
|
||||
meta.SubCmdName = c.Subcommand()
|
||||
return &command.DomainDisableCommand{Meta: meta}, nil
|
||||
},
|
||||
"domainenable": func() (cli.Command, error) {
|
||||
meta.SubCmdName = c.Subcommand()
|
||||
return &command.DomainEnableCommand{Meta: meta}, nil
|
||||
},
|
||||
"aliasdomains": func() (cli.Command, error) {
|
||||
meta.SubCmdName = c.Subcommand()
|
||||
return &command.AliasDomainsCommand{Meta: meta}, nil
|
||||
1
const.go
1
const.go
@@ -5,6 +5,7 @@ const (
|
||||
DirNameConfig = ".mailfull"
|
||||
FileNameConfig = "config"
|
||||
|
||||
FileNameDomainDisable = ".vdomaindisable"
|
||||
FileNameAliasDomains = ".valiasdomains"
|
||||
FileNameUsersPassword = ".vpasswd"
|
||||
FileNameUserForwards = ".forward"
|
||||
|
||||
24
database.go
24
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
|
||||
|
||||
5
doc/README.md
Normal file
5
doc/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
Documentation
|
||||
=============
|
||||
|
||||
- [Configuration](configuration.md)
|
||||
- [Migrating from mailfull](migrating_from_mailfull.md)
|
||||
12
doc/configuration.md
Normal file
12
doc/configuration.md
Normal file
@@ -0,0 +1,12 @@
|
||||
Configuration
|
||||
=============
|
||||
|
||||
`.mailfull/config`
|
||||
|
||||
| key | type | default | required | description |
|
||||
|:----|:-----|:--------|:---------|:------------|
|
||||
| dir_database | string | `"./etc"` | no | A relative path from repository dir (or a absolute path) |
|
||||
| dir_maildata | string | `"./domains"` | no | A relative path from repository dir (or a absolute path) |
|
||||
| username | string | The username who executed `mailfull init` | **yes** | It used for setting owner of database files and maildata files. |
|
||||
| cmd_postalias | string | `"postalias"` | no | Command name or path |
|
||||
| cmd_postmap | string | `"postmap"` | no | Command name or path |
|
||||
24
doc/migrating_from_mailfull.md
Normal file
24
doc/migrating_from_mailfull.md
Normal file
@@ -0,0 +1,24 @@
|
||||
Migrating from mailfull
|
||||
=======================
|
||||
|
||||
Migrating from [directorz/mailfull](https://github.com/directorz/mailfull)
|
||||
|
||||
Change directory to Mailfull directory.
|
||||
|
||||
```
|
||||
# su - mailfull
|
||||
$ cd /home/mailfull
|
||||
```
|
||||
|
||||
Initialize a directory as a Mailfull repository.
|
||||
|
||||
```
|
||||
$ mailfull init
|
||||
```
|
||||
|
||||
Delete unnecessary files.
|
||||
|
||||
```
|
||||
$ rm -rf .git .gitignore bin docs lib README.md README.ja.md
|
||||
$ find domains -maxdepth 2 -name '.vforward' | xargs rm -f
|
||||
```
|
||||
121
domain.go
121
domain.go
@@ -11,6 +11,7 @@ import (
|
||||
// Domain represents a Domain.
|
||||
type Domain struct {
|
||||
name string
|
||||
disabled bool
|
||||
Users []*User
|
||||
AliasUsers []*AliasUser
|
||||
CatchAllUser *CatchAllUser
|
||||
@@ -25,22 +26,41 @@ func (p DomainSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
|
||||
// NewDomain creates a new Domain instance.
|
||||
func NewDomain(name string) (*Domain, error) {
|
||||
if !validDomainName(name) {
|
||||
return nil, ErrInvalidDomainName
|
||||
}
|
||||
d := &Domain{}
|
||||
|
||||
d := &Domain{
|
||||
name: name,
|
||||
if err := d.setName(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// setName sets the name.
|
||||
func (d *Domain) setName(name string) error {
|
||||
if !validDomainName(name) {
|
||||
return ErrInvalidDomainName
|
||||
}
|
||||
|
||||
d.name = name
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Name returns name.
|
||||
func (d *Domain) Name() string {
|
||||
return d.name
|
||||
}
|
||||
|
||||
// SetDisabled disables the Domain if the input is true.
|
||||
func (d *Domain) SetDisabled(disabled bool) {
|
||||
d.disabled = disabled
|
||||
}
|
||||
|
||||
// Disabled returns true if the Domain is disabled.
|
||||
func (d *Domain) Disabled() bool {
|
||||
return d.disabled
|
||||
}
|
||||
|
||||
// Domains returns a Domain slice.
|
||||
func (r *Repository) Domains() ([]*Domain, error) {
|
||||
fileInfos, err := ioutil.ReadDir(r.DirMailDataPath)
|
||||
@@ -62,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)
|
||||
}
|
||||
|
||||
@@ -94,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())
|
||||
@@ -150,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
|
||||
}
|
||||
|
||||
@@ -182,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
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ smtpd_sasl_path = private/auth
|
||||
smtpd_tls_cert_file = /etc/pki/dovecot/certs/dovecot.pem
|
||||
smtpd_tls_key_file = /etc/pki/dovecot/private/dovecot.pem
|
||||
#smtpd_tls_CAfile =
|
||||
smtpd_tls_session_cache_database = btree:/etc/postfix/smtpd_scache
|
||||
smtpd_tls_session_cache_database = btree:/var/lib/postfix/smtpd_scache
|
||||
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
|
||||
|
||||
smtp_tls_security_level = may
|
||||
|
||||
@@ -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.
|
||||
@@ -126,10 +127,10 @@ func OpenRepository(basePath string) (*Repository, error) {
|
||||
for {
|
||||
configDirPath := filepath.Join(rootPath, DirNameConfig)
|
||||
|
||||
fi, errStat := os.Stat(configDirPath)
|
||||
if errStat != nil {
|
||||
if errStat.(*os.PathError).Err != syscall.ENOENT {
|
||||
return nil, errStat
|
||||
fi, err := os.Stat(configDirPath)
|
||||
if err != nil {
|
||||
if err.(*os.PathError).Err != syscall.ENOENT {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if fi.IsDir() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package mailfull
|
||||
|
||||
// Version is a version number.
|
||||
const Version = "0.0.2"
|
||||
const Version = "v0.0.4"
|
||||
|
||||
Reference in New Issue
Block a user