1
0
mirror of https://github.com/directorz/mailfull-go.git synced 2025-12-20 11:07:02 +00:00

22 Commits

Author SHA1 Message Date
teru
db554a1da1 Merge pull request #18 from directorz/feature/no_commit_flag
Feature/no commit flag
2016-08-28 17:12:46 +09:00
teru
708e132ccc Bump version to v0.0.5 2016-08-28 17:10:32 +09:00
teru
3a1d4a588c Add a option to subcommands #7 2016-08-28 17:09:09 +09:00
teru
50d429ad78 Implement to parse -n flag #7 2016-08-28 17:06:52 +09:00
teru
0118652074 Merge pull request #17 from directorz/feature/implements
Feature/implements
2016-08-23 18:08:22 +09:00
teru
bf4b63556a Bump version to v0.0.4 2016-08-23 18:05:52 +09:00
teru
24ee3c26b0 Rename a file 2016-08-23 18:04:55 +09:00
teru
49dd05f3bf Change output format of domains subcommands #8 2016-08-23 18:04:30 +09:00
teru
009ee84e63 Implement subcommands: domaindisable, domainenable #8 2016-08-23 18:04:08 +09:00
teru
281d5a4a9d Implement disable/enable a Domain #8 2016-08-23 18:03:45 +09:00
teru
c2dca41057 Add a field to Domain struct 2016-08-23 13:40:20 +09:00
teru
4f99f11732 Update installation guide 2016-08-23 13:39:35 +09:00
teru
a3d3684fda Merge pull request #16 from directorz/feature/implements
Feature/implements
2016-08-22 15:20:37 +09:00
teru
c183bba7aa Bump version to v0.0.3 2016-08-22 15:19:59 +09:00
teru
b52a7cde7f Add support for creating GitHub Releases page 2016-08-22 15:15:08 +09:00
teru
0926ffd318 Change the path of session cache database 2016-08-18 20:33:19 +09:00
teru
c9201c8fc1 Fix README.md 2016-08-18 19:53:43 +09:00
teru
fe16bb86f9 Disable to delete postmaster #14 2016-08-18 19:53:16 +09:00
teru
bd6228197a cosmetic changes 2016-08-18 19:52:42 +09:00
teru
1329747f54 Merge pull request #15 from directorz/feature/documentation
Feature/documentation
2016-08-18 12:18:11 +09:00
teru
ab3c3315f9 Add configuration guide 2016-08-18 12:11:17 +09:00
teru
a76596c348 Add migration guide from directorz/mailfull #9 2016-08-18 12:09:44 +09:00
31 changed files with 691 additions and 42 deletions

7
.gitignore vendored
View File

@@ -1 +1,6 @@
/cli/mailfull/mailfull
/vendor
/build
/release
/github_token
/cmd/mailfull/mailfull

6
Gomfile Normal file
View 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
View 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)

View File

@@ -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)

View File

@@ -20,7 +20,7 @@ func (c *AliasDomainAddCommand) Synopsis() string {
func (c *AliasDomainAddCommand) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s domain target
%s %s [-n] domain target
Description:
%s
@@ -30,6 +30,10 @@ Required Args:
The domain name that you want to create.
target
The target domain name.
Optional Args:
-n
Don't update databases.
`,
c.CmdName, c.SubCmdName,
c.Synopsis())
@@ -39,6 +43,12 @@ Required Args:
// Run runs the command and returns the exit status.
func (c *AliasDomainAddCommand) Run(args []string) int {
noCommit, err := noCommitFlag(&args)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
}
if len(args) != 2 {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
@@ -64,6 +74,10 @@ func (c *AliasDomainAddCommand) Run(args []string) int {
return 1
}
if noCommit {
return 0
}
mailData, err := repo.MailData()
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)

View File

@@ -20,7 +20,7 @@ func (c *AliasDomainDelCommand) Synopsis() string {
func (c *AliasDomainDelCommand) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s domain
%s %s [-n] domain
Description:
%s
@@ -28,6 +28,10 @@ Description:
Required Args:
domain
The domain name that you want to delete.
Optional Args:
-n
Don't update databases.
`,
c.CmdName, c.SubCmdName,
c.Synopsis())
@@ -37,6 +41,12 @@ Required Args:
// Run runs the command and returns the exit status.
func (c *AliasDomainDelCommand) Run(args []string) int {
noCommit, err := noCommitFlag(&args)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
}
if len(args) != 1 {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
@@ -55,6 +65,10 @@ func (c *AliasDomainDelCommand) Run(args []string) int {
return 1
}
if noCommit {
return 0
}
mailData, err := repo.MailData()
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)

View File

@@ -21,7 +21,7 @@ func (c *AliasUserAddCommand) Synopsis() string {
func (c *AliasUserAddCommand) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s address target [target...]
%s %s [-n] address target [target...]
Description:
%s
@@ -31,6 +31,10 @@ Required Args:
The email address that you want to create.
target
Target email addresses.
Optional Args:
-n
Don't update databases.
`,
c.CmdName, c.SubCmdName,
c.Synopsis())
@@ -40,6 +44,12 @@ Required Args:
// Run runs the command and returns the exit status.
func (c *AliasUserAddCommand) Run(args []string) int {
noCommit, err := noCommitFlag(&args)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
}
if len(args) < 2 {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
@@ -73,6 +83,10 @@ func (c *AliasUserAddCommand) Run(args []string) int {
return 1
}
if noCommit {
return 0
}
mailData, err := repo.MailData()
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)

View File

@@ -21,7 +21,7 @@ func (c *AliasUserDelCommand) Synopsis() string {
func (c *AliasUserDelCommand) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s address
%s %s [-n] address
Description:
%s
@@ -29,6 +29,10 @@ Description:
Required Args:
address
The email address that you want to delete.
Optional Args:
-n
Don't update databases.
`,
c.CmdName, c.SubCmdName,
c.Synopsis())
@@ -38,6 +42,12 @@ Required Args:
// Run runs the command and returns the exit status.
func (c *AliasUserDelCommand) Run(args []string) int {
noCommit, err := noCommitFlag(&args)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
}
if len(args) != 1 {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
@@ -63,6 +73,10 @@ func (c *AliasUserDelCommand) Run(args []string) int {
return 1
}
if noCommit {
return 0
}
mailData, err := repo.MailData()
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)

View File

@@ -21,7 +21,7 @@ func (c *AliasUserModCommand) Synopsis() string {
func (c *AliasUserModCommand) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s address target [target...]
%s %s [-n] address target [target...]
Description:
%s
@@ -31,6 +31,10 @@ Required Args:
The email address that you want to modify.
target
Target email addresses.
Optional Args:
-n
Don't update databases.
`,
c.CmdName, c.SubCmdName,
c.Synopsis())
@@ -40,6 +44,12 @@ Required Args:
// Run runs the command and returns the exit status.
func (c *AliasUserModCommand) Run(args []string) int {
noCommit, err := noCommitFlag(&args)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
}
if len(args) < 2 {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
@@ -82,6 +92,10 @@ func (c *AliasUserModCommand) Run(args []string) int {
return 1
}
if noCommit {
return 0
}
mailData, err := repo.MailData()
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)

View File

@@ -20,7 +20,7 @@ func (c *CatchAllSetCommand) Synopsis() string {
func (c *CatchAllSetCommand) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s domain user
%s %s [-n] domain user
Description:
%s
@@ -30,6 +30,10 @@ Required Args:
The domain name.
user
The user name that you want to set as catchall user.
Optional Args:
-n
Don't update databases.
`,
c.CmdName, c.SubCmdName,
c.Synopsis())
@@ -39,6 +43,12 @@ Required Args:
// Run runs the command and returns the exit status.
func (c *CatchAllSetCommand) Run(args []string) int {
noCommit, err := noCommitFlag(&args)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
}
if len(args) != 2 {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
@@ -64,6 +74,10 @@ func (c *CatchAllSetCommand) Run(args []string) int {
return 1
}
if noCommit {
return 0
}
mailData, err := repo.MailData()
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)

View File

@@ -20,7 +20,7 @@ func (c *CatchAllUnsetCommand) Synopsis() string {
func (c *CatchAllUnsetCommand) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s domain
%s %s [-n] domain
Description:
%s
@@ -28,6 +28,10 @@ Description:
Required Args:
domain
The domain name.
Optional Args:
-n
Don't update databases.
`,
c.CmdName, c.SubCmdName,
c.Synopsis())
@@ -37,6 +41,12 @@ Required Args:
// Run runs the command and returns the exit status.
func (c *CatchAllUnsetCommand) Run(args []string) int {
noCommit, err := noCommitFlag(&args)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
}
if len(args) != 1 {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
@@ -55,6 +65,10 @@ func (c *CatchAllUnsetCommand) Run(args []string) int {
return 1
}
if noCommit {
return 0
}
mailData, err := repo.MailData()
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)

View File

@@ -20,7 +20,7 @@ func (c *DomainAddCommand) Synopsis() string {
func (c *DomainAddCommand) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s domain
%s %s [-n] domain
Description:
%s
@@ -28,6 +28,10 @@ Description:
Required Args:
domain
The domain name that you want to create.
Optional Args:
-n
Don't update databases.
`,
c.CmdName, c.SubCmdName,
c.Synopsis())
@@ -37,6 +41,12 @@ Required Args:
// Run runs the command and returns the exit status.
func (c *DomainAddCommand) Run(args []string) int {
noCommit, err := noCommitFlag(&args)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
}
if len(args) != 1 {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
@@ -72,6 +82,10 @@ func (c *DomainAddCommand) Run(args []string) int {
return 1
}
if noCommit {
return 0
}
mailData, err := repo.MailData()
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)

View File

@@ -20,7 +20,7 @@ func (c *DomainDelCommand) Synopsis() string {
func (c *DomainDelCommand) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s domain
%s %s [-n] domain
Description:
%s
@@ -28,6 +28,10 @@ Description:
Required Args:
domain
The domain name that you want to delete.
Optional Args:
-n
Don't update databases.
`,
c.CmdName, c.SubCmdName,
c.Synopsis())
@@ -37,6 +41,12 @@ Required Args:
// Run runs the command and returns the exit status.
func (c *DomainDelCommand) Run(args []string) int {
noCommit, err := noCommitFlag(&args)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
}
if len(args) != 1 {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
@@ -55,6 +65,10 @@ func (c *DomainDelCommand) Run(args []string) int {
return 1
}
if noCommit {
return 0
}
mailData, err := repo.MailData()
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)

View File

@@ -0,0 +1,97 @@
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 [-n] domain
Description:
%s
Required Args:
domain
The domain name that you want to disable.
Optional Args:
-n
Don't update databases.
`,
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 {
noCommit, err := noCommitFlag(&args)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
}
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
}
if noCommit {
return 0
}
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
}

View File

@@ -0,0 +1,97 @@
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 [-n] domain
Description:
%s
Required Args:
domain
The domain name that you want to enable.
Optional Args:
-n
Don't update databases.
`,
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 {
noCommit, err := noCommitFlag(&args)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
}
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
}
if noCommit {
return 0
}
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
}

View File

@@ -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

View File

@@ -1,6 +1,9 @@
package command
import (
"bytes"
"flag"
"github.com/mitchellh/cli"
)
@@ -11,3 +14,17 @@ type Meta struct {
SubCmdName string
Version string
}
// noCommitFlag returns true if `pargs` has "-n" flag.
// `pargs` is overwrites with non-flag arguments.
func noCommitFlag(pargs *[]string) (bool, error) {
nFlag := false
flagSet := flag.NewFlagSet("", flag.ContinueOnError)
flagSet.SetOutput(&bytes.Buffer{})
flagSet.BoolVar(&nFlag, "n", nFlag, "")
err := flagSet.Parse(*pargs)
*pargs = flagSet.Args()
return nFlag, err
}

View File

@@ -21,7 +21,7 @@ func (c *UserAddCommand) Synopsis() string {
func (c *UserAddCommand) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s address
%s %s [-n] address
Description:
%s
@@ -29,6 +29,10 @@ Description:
Required Args:
address
The email address that you want to create.
Optional Args:
-n
Don't update databases.
`,
c.CmdName, c.SubCmdName,
c.Synopsis())
@@ -38,6 +42,12 @@ Required Args:
// Run runs the command and returns the exit status.
func (c *UserAddCommand) Run(args []string) int {
noCommit, err := noCommitFlag(&args)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
}
if len(args) != 1 {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
@@ -70,6 +80,10 @@ func (c *UserAddCommand) Run(args []string) int {
return 1
}
if noCommit {
return 0
}
mailData, err := repo.MailData()
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)

View File

@@ -34,7 +34,7 @@ Required Args:
Optional Args:
password
Specify the password instead of your typing.
This option is not recommended because the password will be visible in your shell history.
This option is NOT recommended because the password will be visible in your shell history.
`,
c.CmdName, c.SubCmdName,
c.Synopsis())
@@ -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
}

View File

@@ -21,7 +21,7 @@ func (c *UserDelCommand) Synopsis() string {
func (c *UserDelCommand) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s address
%s %s [-n] address
Description:
%s
@@ -29,6 +29,10 @@ Description:
Required Args:
address
The email address that you want to delete.
Optional Args:
-n
Don't update databases.
`,
c.CmdName, c.SubCmdName,
c.Synopsis())
@@ -38,6 +42,12 @@ Required Args:
// Run runs the command and returns the exit status.
func (c *UserDelCommand) Run(args []string) int {
noCommit, err := noCommitFlag(&args)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
}
if len(args) != 1 {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
@@ -59,11 +69,20 @@ 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
}
if noCommit {
return 0
}
mailData, err := repo.MailData()
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)

View File

@@ -22,7 +22,7 @@ func (c *UserPasswdCommand) Synopsis() string {
func (c *UserPasswdCommand) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s address [password]
%s %s [-n] address [password]
Description:
%s
@@ -32,9 +32,11 @@ Required Args:
The email address that you want to update the password.
Optional Args:
-n
Don't update databases.
password
Specify the password instead of your typing.
This option is not recommended because the password will be visible in your shell history.
This option is NOT recommended because the password will be visible in your shell history.
`,
c.CmdName, c.SubCmdName,
c.Synopsis())
@@ -44,6 +46,12 @@ Optional Args:
// Run runs the command and returns the exit status.
func (c *UserPasswdCommand) Run(args []string) int {
noCommit, err := noCommitFlag(&args)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
}
if len(args) != 1 && len(args) != 2 {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
@@ -81,14 +89,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 +108,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
@@ -115,6 +123,10 @@ func (c *UserPasswdCommand) Run(args []string) int {
return 1
}
if noCommit {
return 0
}
mailData, err := repo.MailData()
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)

View File

@@ -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

View File

@@ -5,6 +5,7 @@ const (
DirNameConfig = ".mailfull"
FileNameConfig = "config"
FileNameDomainDisable = ".vdomaindisable"
FileNameAliasDomains = ".valiasdomains"
FileNameUsersPassword = ".vpasswd"
FileNameUserForwards = ".forward"

View File

@@ -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
View File

@@ -0,0 +1,5 @@
Documentation
=============
- [Configuration](configuration.md)
- [Migrating from mailfull](migrating_from_mailfull.md)

12
doc/configuration.md Normal file
View 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 |

View 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
View File

@@ -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
}

View File

@@ -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

View File

@@ -34,6 +34,7 @@ var (
ErrAliasUserNotExist = errors.New("AliasUser: not exist")
ErrAliasUserAlreadyExist = errors.New("AliasUser: already exist")
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")
@@ -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() {

View File

@@ -1,4 +1,4 @@
package mailfull
// Version is a version number.
const Version = "0.0.2"
const Version = "v0.0.5"