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

52 Commits

Author SHA1 Message Date
teru
3ae4d8d590 Merge pull request #30 from directorz/feature/v1.0.0
Feature/v1.0.0
2017-07-25 13:05:27 +09:00
teru
d4343eacce Merge pull request #29 from directorz/feature/bump_version
Bump version to v1.0.0
2017-07-25 13:05:04 +09:00
teru
a97a9e343b Bump version to v1.0.0 2017-07-25 13:04:25 +09:00
teru
f61d18f31f Merge pull request #28 from directorz/feature/remove_type_maildata
Remove type MailData
2017-07-25 13:02:30 +09:00
teru
9c92ac9656 Remove type MailData 2017-07-25 12:48:54 +09:00
teru
30f0279751 Merge pull request #27 from directorz/feature/remove_types_for_sort
Remove types for sort.Interface #26
2017-07-25 12:31:39 +09:00
teru
c7e56f26af Remove types for sort.Interface 2017-07-25 12:29:23 +09:00
teru
58b50cb3f6 Merge pull request #25 from directorz/feature/flatten
Feature/flatten
2017-07-25 11:58:26 +09:00
teru
146ab299d0 Use Errorf 2017-07-25 11:57:30 +09:00
teru
2e87209ad2 Move Meta to cmd package 2017-07-25 11:40:51 +09:00
teru
5a178c350b Change structs name 2017-07-25 11:35:42 +09:00
teru
b884655a7c Renamed 2017-07-25 11:22:11 +09:00
teru
552ed95a0f Integrate subpackage 2017-07-25 11:19:25 +09:00
teru
2bb46731d0 Merge pull request #24 from directorz/feature/cosme
cosmetic
2017-07-25 11:06:30 +09:00
teru
5a64a543ca cosmetic 2017-07-25 11:05:28 +09:00
teru
d0dd68e2b6 Merge pull request #23 from directorz/feature/build
Feature/build
2017-07-25 11:03:49 +09:00
teru
1daea44e49 Use golang:1.8.3 2017-07-25 11:01:55 +09:00
teru
3ac5856917 Use golang/dep instead of glide #21 2017-07-25 10:43:07 +09:00
teru
3cf4815565 Merge pull request #20 from directorz/feature/update_golang
Feature/update golang
2016-12-19 13:25:09 +09:00
teru
52e5d32c01 Bump version to v0.0.7 2016-12-19 13:24:41 +09:00
teru
b1d13b49f5 Use golang:1.7.4 2016-12-19 13:24:16 +09:00
teru
46cf349015 Merge pull request #19 from directorz/feature/vendoring_glide
Feature/vendoring glide
2016-09-29 12:12:28 +09:00
teru
9be507565d Bump version to v0.0.6 2016-09-29 12:12:03 +09:00
teru
58b4e49fe8 Use golang:1.7.1 2016-09-29 12:10:15 +09:00
teru
cb6e9ce2c5 Use Glide instead of gom 2016-09-29 12:08:07 +09:00
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
teru
65f9c77e0d Merge pull request #13 from directorz/feature/implements
Feature/implements
2016-08-16 00:40:05 +09:00
teru
34768d90f1 Bump version to 0.0.2 2016-08-16 00:39:42 +09:00
teru
798579eda8 Add a link to godoc 2016-08-16 00:35:11 +09:00
teru
a5f0184852 Change configuration key names #12 2016-08-16 00:32:54 +09:00
teru
d0003ee454 Add LICENSE 2016-08-16 00:31:45 +09:00
47 changed files with 1196 additions and 583 deletions

7
.gitignore vendored
View File

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

48
Gopkg.lock generated Normal file
View File

@@ -0,0 +1,48 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "github.com/BurntSushi/toml"
packages = ["."]
revision = "99064174e013895bbd9b025c31100bd1d9b590ca"
version = "v0.3.0"
[[projects]]
branch = "master"
name = "github.com/armon/go-radix"
packages = ["."]
revision = "4239b77079c7b5d1243b7b4736304ce8ddb6f0f2"
[[projects]]
name = "github.com/bgentry/speakeasy"
packages = ["."]
revision = "a1ccbf2c40dfc8ce514b5c5c6e6d1429ea6880da"
[[projects]]
branch = "master"
name = "github.com/jsimonetti/pwscheme"
packages = ["ssha"]
revision = "76804708ecad54773871b35dbaa44f517973e395"
[[projects]]
name = "github.com/mattn/go-isatty"
packages = ["."]
revision = "66b8e73f3f5cda9f96b69efd03dd3d7fc4a5cdb8"
[[projects]]
branch = "master"
name = "github.com/mitchellh/cli"
packages = ["."]
revision = "fcf521421aa29bde1d93b6920dfce826d7932208"
[[projects]]
name = "golang.org/x/sys"
packages = ["unix"]
revision = "8f0908ab3b2457e2e15403d3697c9ef5cb4b57a9"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "d88d2668ae10b500606e3ebb1e719897e62e423f5ede8e4b470d12c1d255fe31"
solver-name = "gps-cdcl"
solver-version = 1

0
Gopkg.toml Normal file
View File

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Hikaru KASHIWAZAKI
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

66
Makefile Normal file
View File

@@ -0,0 +1,66 @@
GOVERSION=$(shell go version)
THIS_GOOS=$(word 1,$(subst /, ,$(lastword $(GOVERSION))))
THIS_GOARCH=$(word 2,$(subst /, ,$(lastword $(GOVERSION))))
GOOS?=$(THIS_GOOS)
GOARCH?=$(THIS_GOARCH)
DIR_PKG=$(subst /src/github.com/directorz/mailfull-go,/pkg,$(PWD))
DIR_BUILD=build
DIR_RELEASE=release
VERSION=$(patsubst "%",%,$(lastword $(shell grep 'const Version' version.go)))
GITTAG=$(shell git rev-parse --short HEAD)
.PHONY: build build-linux-amd64 build-linux-386 clean
default: build
$(DIR_BUILD)/bin/$(THIS_GOOS)_$(THIS_GOARCH)/dep:
mkdir -p /tmp/go
GOPATH=/tmp/go go get -d -v github.com/golang/dep
GOPATH=/tmp/go go build -v -o $(DIR_BUILD)/bin/$(THIS_GOOS)_$(THIS_GOARCH)/dep github.com/golang/dep/cmd/dep
rm -rf /tmp/go
dep: $(DIR_BUILD)/bin/$(THIS_GOOS)_$(THIS_GOARCH)/dep
installdeps: dep
$(DIR_BUILD)/bin/$(THIS_GOOS)_$(THIS_GOARCH)/dep ensure -v
build:
go build -v -i -ldflags "-X main.gittag=$(GITTAG)" -o $(DIR_BUILD)/mailfull_$(GOOS)_$(GOARCH)/mailfull cmd/mailfull/*.go
.build-docker:
docker run --rm -v $(DIR_PKG):/go/pkg -v $(PWD):/go/src/github.com/directorz/mailfull-go -w /go/src/github.com/directorz/mailfull-go \
-e GOOS=$(GOOS) -e GOARCH=$(GOARCH) golang:1.8.3 \
go build -v -i -ldflags "-X main.gittag=$(GITTAG)" -o $(DIR_BUILD)/mailfull_$(GOOS)_$(GOARCH)/mailfull cmd/mailfull/*.go
build-linux-amd64:
@$(MAKE) .build-docker GOOS=linux GOARCH=amd64
build-linux-386:
@$(MAKE) .build-docker GOOS=linux GOARCH=386
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

@@ -3,6 +3,8 @@ mailfull-go
A management tool for virtual domain email for Postfix and Dovecot written in Go.
[![GoDoc](https://godoc.org/github.com/directorz/mailfull-go?status.svg)](https://godoc.org/github.com/directorz/mailfull-go)
Features
--------
@@ -14,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`
@@ -37,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.)
@@ -64,3 +72,8 @@ Add a new domain and user.
```
Enjoy!
More info
---------
See [documentation](doc/README.md)

View File

@@ -15,13 +15,6 @@ type AliasDomain struct {
target string
}
// AliasDomainSlice attaches the methods of sort.Interface to []*AliasDomain.
type AliasDomainSlice []*AliasDomain
func (p AliasDomainSlice) Len() int { return len(p) }
func (p AliasDomainSlice) Less(i, j int) bool { return p[i].Name() < p[j].Name() }
func (p AliasDomainSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
// NewAliasDomain creates a new AliasDomain instance.
func NewAliasDomain(name, target string) (*AliasDomain, error) {
ad := &AliasDomain{}
@@ -190,7 +183,7 @@ func (r *Repository) writeAliasDomainsFile(aliasDomains []*AliasDomain) error {
}
defer file.Close()
sort.Sort(AliasDomainSlice(aliasDomains))
sort.Slice(aliasDomains, func(i, j int) bool { return aliasDomains[i].Name() < aliasDomains[j].Name() })
for _, aliasDomain := range aliasDomains {
if _, err := fmt.Fprintf(file, "%s:%s\n", aliasDomain.Name(), aliasDomain.Target()); err != nil {

View File

@@ -21,13 +21,6 @@ type AliasUser struct {
targets []string
}
// AliasUserSlice attaches the methods of sort.Interface to []*AliasUser.
type AliasUserSlice []*AliasUser
func (p AliasUserSlice) Len() int { return len(p) }
func (p AliasUserSlice) Less(i, j int) bool { return p[i].Name() < p[j].Name() }
func (p AliasUserSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
// NewAliasUser creates a new AliasUser instance.
func NewAliasUser(name string, targets []string) (*AliasUser, error) {
au := &AliasUser{}
@@ -233,7 +226,7 @@ func (r *Repository) writeAliasUsersFile(domainName string, aliasUsers []*AliasU
}
defer file.Close()
sort.Sort(AliasUserSlice(aliasUsers))
sort.Slice(aliasUsers, func(i, j int) bool { return aliasUsers[i].Name() < aliasUsers[j].Name() })
for _, aliasUser := range aliasUsers {
if _, err := fmt.Fprintf(file, "%s:%s\n", aliasUser.Name(), strings.Join(aliasUser.Targets(), ",")); err != nil {

View File

@@ -1,26 +1,27 @@
package command
package main
import (
"fmt"
mailfull "github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go/cmd"
)
// AliasDomainAddCommand represents a AliasDomainAddCommand.
type AliasDomainAddCommand struct {
Meta
// CmdAliasDomainAdd represents a CmdAliasDomainAdd.
type CmdAliasDomainAdd struct {
cmd.Meta
}
// Synopsis returns a one-line synopsis.
func (c *AliasDomainAddCommand) Synopsis() string {
func (c *CmdAliasDomainAdd) Synopsis() string {
return "Create a new aliasdomain."
}
// Help returns long-form help text.
func (c *AliasDomainAddCommand) Help() string {
func (c *CmdAliasDomainAdd) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s domain target
%s %s [-n] domain target
Description:
%s
@@ -30,6 +31,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())
@@ -38,7 +43,13 @@ Required Args:
}
// Run runs the command and returns the exit status.
func (c *AliasDomainAddCommand) Run(args []string) int {
func (c *CmdAliasDomainAdd) 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
@@ -49,30 +60,26 @@ func (c *AliasDomainAddCommand) Run(args []string) int {
repo, err := mailfull.OpenRepository(".")
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
aliasDomain, err := mailfull.NewAliasDomain(aliasDomainName, targetDomainName)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
if err := repo.AliasDomainCreate(aliasDomain); err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
mailData, err := repo.MailData()
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
return 1
if noCommit {
return 0
}
err = repo.GenerateDatabases(mailData)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
if err = repo.GenerateDatabases(); err != nil {
c.Meta.Errorf("%v\n", err)
return 1
}

View File

@@ -1,26 +1,27 @@
package command
package main
import (
"fmt"
mailfull "github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go/cmd"
)
// AliasDomainDelCommand represents a AliasDomainDelCommand.
type AliasDomainDelCommand struct {
Meta
// CmdAliasDomainDel represents a CmdAliasDomainDel.
type CmdAliasDomainDel struct {
cmd.Meta
}
// Synopsis returns a one-line synopsis.
func (c *AliasDomainDelCommand) Synopsis() string {
func (c *CmdAliasDomainDel) Synopsis() string {
return "Delete a aliasdomain."
}
// Help returns long-form help text.
func (c *AliasDomainDelCommand) Help() string {
func (c *CmdAliasDomainDel) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s domain
%s %s [-n] domain
Description:
%s
@@ -28,6 +29,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())
@@ -36,7 +41,13 @@ Required Args:
}
// Run runs the command and returns the exit status.
func (c *AliasDomainDelCommand) Run(args []string) int {
func (c *CmdAliasDomainDel) 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
@@ -46,24 +57,20 @@ func (c *AliasDomainDelCommand) Run(args []string) int {
repo, err := mailfull.OpenRepository(".")
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
if err := repo.AliasDomainRemove(aliasDomainName); err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
mailData, err := repo.MailData()
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
return 1
if noCommit {
return 0
}
err = repo.GenerateDatabases(mailData)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
if err = repo.GenerateDatabases(); err != nil {
c.Meta.Errorf("%v\n", err)
return 1
}

View File

@@ -1,24 +1,25 @@
package command
package main
import (
"fmt"
"sort"
mailfull "github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go/cmd"
)
// AliasDomainsCommand represents a AliasDomainsCommand.
type AliasDomainsCommand struct {
Meta
// CmdAliasDomains represents a CmdAliasDomains.
type CmdAliasDomains struct {
cmd.Meta
}
// Synopsis returns a one-line synopsis.
func (c *AliasDomainsCommand) Synopsis() string {
func (c *CmdAliasDomains) Synopsis() string {
return "Show aliasdomains."
}
// Help returns long-form help text.
func (c *AliasDomainsCommand) Help() string {
func (c *CmdAliasDomains) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s [domain]
@@ -37,7 +38,7 @@ Optional Args:
}
// Run runs the command and returns the exit status.
func (c *AliasDomainsCommand) Run(args []string) int {
func (c *CmdAliasDomains) Run(args []string) int {
if len(args) > 1 {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
@@ -50,16 +51,16 @@ func (c *AliasDomainsCommand) Run(args []string) int {
repo, err := mailfull.OpenRepository(".")
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
aliasDomains, err := repo.AliasDomains()
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
sort.Sort(mailfull.AliasDomainSlice(aliasDomains))
sort.Slice(aliasDomains, func(i, j int) bool { return aliasDomains[i].Name() < aliasDomains[j].Name() })
for _, aliasDomain := range aliasDomains {
if targetDomainName != "" {

View File

@@ -1,27 +1,28 @@
package command
package main
import (
"fmt"
"strings"
mailfull "github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go/cmd"
)
// AliasUserAddCommand represents a AliasUserAddCommand.
type AliasUserAddCommand struct {
Meta
// CmdAliasUserAdd represents a CmdAliasUserAdd.
type CmdAliasUserAdd struct {
cmd.Meta
}
// Synopsis returns a one-line synopsis.
func (c *AliasUserAddCommand) Synopsis() string {
func (c *CmdAliasUserAdd) Synopsis() string {
return "Create a new aliasuser."
}
// Help returns long-form help text.
func (c *AliasUserAddCommand) Help() string {
func (c *CmdAliasUserAdd) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s address target [target...]
%s %s [-n] address target [target...]
Description:
%s
@@ -31,6 +32,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())
@@ -39,7 +44,13 @@ Required Args:
}
// Run runs the command and returns the exit status.
func (c *AliasUserAddCommand) Run(args []string) int {
func (c *CmdAliasUserAdd) 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
@@ -58,30 +69,26 @@ func (c *AliasUserAddCommand) Run(args []string) int {
repo, err := mailfull.OpenRepository(".")
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
aliasUser, err := mailfull.NewAliasUser(aliasUserName, targets)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
if err := repo.AliasUserCreate(domainName, aliasUser); err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
mailData, err := repo.MailData()
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
return 1
if noCommit {
return 0
}
err = repo.GenerateDatabases(mailData)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
if err = repo.GenerateDatabases(); err != nil {
c.Meta.Errorf("%v\n", err)
return 1
}

View File

@@ -1,27 +1,28 @@
package command
package main
import (
"fmt"
"strings"
mailfull "github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go/cmd"
)
// AliasUserDelCommand represents a AliasUserDelCommand.
type AliasUserDelCommand struct {
Meta
// CmdAliasUserDel represents a CmdAliasUserDel.
type CmdAliasUserDel struct {
cmd.Meta
}
// Synopsis returns a one-line synopsis.
func (c *AliasUserDelCommand) Synopsis() string {
func (c *CmdAliasUserDel) Synopsis() string {
return "Delete a aliasuser."
}
// Help returns long-form help text.
func (c *AliasUserDelCommand) Help() string {
func (c *CmdAliasUserDel) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s address
%s %s [-n] address
Description:
%s
@@ -29,6 +30,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())
@@ -37,7 +42,13 @@ Required Args:
}
// Run runs the command and returns the exit status.
func (c *AliasUserDelCommand) Run(args []string) int {
func (c *CmdAliasUserDel) 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
@@ -54,24 +65,20 @@ func (c *AliasUserDelCommand) Run(args []string) int {
repo, err := mailfull.OpenRepository(".")
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
if err := repo.AliasUserRemove(domainName, aliasUserName); err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
mailData, err := repo.MailData()
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
return 1
if noCommit {
return 0
}
err = repo.GenerateDatabases(mailData)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
if err = repo.GenerateDatabases(); err != nil {
c.Meta.Errorf("%v\n", err)
return 1
}

View File

@@ -1,27 +1,28 @@
package command
package main
import (
"fmt"
"strings"
mailfull "github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go/cmd"
)
// AliasUserModCommand represents a AliasUserModCommand.
type AliasUserModCommand struct {
Meta
// CmdAliasUserMod represents a CmdAliasUserMod.
type CmdAliasUserMod struct {
cmd.Meta
}
// Synopsis returns a one-line synopsis.
func (c *AliasUserModCommand) Synopsis() string {
func (c *CmdAliasUserMod) Synopsis() string {
return "Modify a aliasuser."
}
// Help returns long-form help text.
func (c *AliasUserModCommand) Help() string {
func (c *CmdAliasUserMod) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s address target [target...]
%s %s [-n] address target [target...]
Description:
%s
@@ -31,6 +32,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())
@@ -39,7 +44,13 @@ Required Args:
}
// Run runs the command and returns the exit status.
func (c *AliasUserModCommand) Run(args []string) int {
func (c *CmdAliasUserMod) 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
@@ -58,39 +69,35 @@ func (c *AliasUserModCommand) Run(args []string) int {
repo, err := mailfull.OpenRepository(".")
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
aliasUser, err := repo.AliasUser(domainName, aliasUserName)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
if aliasUser == nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", mailfull.ErrAliasUserNotExist)
c.Meta.Errorf("%v\n", mailfull.ErrAliasUserNotExist)
return 1
}
if err := aliasUser.SetTargets(targets); err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
if err := repo.AliasUserUpdate(domainName, aliasUser); err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
mailData, err := repo.MailData()
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
return 1
if noCommit {
return 0
}
err = repo.GenerateDatabases(mailData)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
if err = repo.GenerateDatabases(); err != nil {
c.Meta.Errorf("%v\n", err)
return 1
}

View File

@@ -1,24 +1,25 @@
package command
package main
import (
"fmt"
"sort"
"github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go/cmd"
)
// AliasUsersCommand represents a AliasUsersCommand.
type AliasUsersCommand struct {
Meta
// CmdAliasUsers represents a CmdAliasUsers.
type CmdAliasUsers struct {
cmd.Meta
}
// Synopsis returns a one-line synopsis.
func (c *AliasUsersCommand) Synopsis() string {
func (c *CmdAliasUsers) Synopsis() string {
return "Show aliasusers."
}
// Help returns long-form help text.
func (c *AliasUsersCommand) Help() string {
func (c *CmdAliasUsers) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s domain
@@ -37,7 +38,7 @@ Required Args:
}
// Run runs the command and returns the exit status.
func (c *AliasUsersCommand) Run(args []string) int {
func (c *CmdAliasUsers) Run(args []string) int {
if len(args) != 1 {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
@@ -47,16 +48,16 @@ func (c *AliasUsersCommand) Run(args []string) int {
repo, err := mailfull.OpenRepository(".")
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
aliasUsers, err := repo.AliasUsers(targetDomainName)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
sort.Sort(mailfull.AliasUserSlice(aliasUsers))
sort.Slice(aliasUsers, func(i, j int) bool { return aliasUsers[i].Name() < aliasUsers[j].Name() })
for _, aliasUser := range aliasUsers {
fmt.Fprintf(c.UI.Writer, "%s\n", aliasUser.Name())

View File

@@ -1,23 +1,24 @@
package command
package main
import (
"fmt"
"github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go/cmd"
)
// CatchAllCommand represents a CatchAllCommand.
type CatchAllCommand struct {
Meta
// CmdCatchAll represents a CmdCatchAll.
type CmdCatchAll struct {
cmd.Meta
}
// Synopsis returns a one-line synopsis.
func (c *CatchAllCommand) Synopsis() string {
func (c *CmdCatchAll) Synopsis() string {
return "Show a catchall user."
}
// Help returns long-form help text.
func (c *CatchAllCommand) Help() string {
func (c *CmdCatchAll) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s domain
@@ -36,7 +37,7 @@ Required Args:
}
// Run runs the command and returns the exit status.
func (c *CatchAllCommand) Run(args []string) int {
func (c *CmdCatchAll) Run(args []string) int {
if len(args) != 1 {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
@@ -46,13 +47,13 @@ func (c *CatchAllCommand) Run(args []string) int {
repo, err := mailfull.OpenRepository(".")
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
catchAllUser, err := repo.CatchAllUser(domainName)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}

View File

@@ -1,26 +1,27 @@
package command
package main
import (
"fmt"
"github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go/cmd"
)
// CatchAllSetCommand represents a CatchAllSetCommand.
type CatchAllSetCommand struct {
Meta
// CmdCatchAllSet represents a CmdCatchAllSet.
type CmdCatchAllSet struct {
cmd.Meta
}
// Synopsis returns a one-line synopsis.
func (c *CatchAllSetCommand) Synopsis() string {
func (c *CmdCatchAllSet) Synopsis() string {
return "Set a catchall user."
}
// Help returns long-form help text.
func (c *CatchAllSetCommand) Help() string {
func (c *CmdCatchAllSet) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s domain user
%s %s [-n] domain user
Description:
%s
@@ -30,6 +31,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())
@@ -38,7 +43,13 @@ Required Args:
}
// Run runs the command and returns the exit status.
func (c *CatchAllSetCommand) Run(args []string) int {
func (c *CmdCatchAllSet) 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
@@ -49,30 +60,26 @@ func (c *CatchAllSetCommand) Run(args []string) int {
repo, err := mailfull.OpenRepository(".")
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
catchAllUser, err := mailfull.NewCatchAllUser(userName)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
if err := repo.CatchAllUserSet(domainName, catchAllUser); err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
mailData, err := repo.MailData()
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
return 1
if noCommit {
return 0
}
err = repo.GenerateDatabases(mailData)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
if err = repo.GenerateDatabases(); err != nil {
c.Meta.Errorf("%v\n", err)
return 1
}

View File

@@ -1,26 +1,27 @@
package command
package main
import (
"fmt"
"github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go/cmd"
)
// CatchAllUnsetCommand represents a CatchAllUnsetCommand.
type CatchAllUnsetCommand struct {
Meta
// CmdCatchAllUnset represents a CmdCatchAllUnset.
type CmdCatchAllUnset struct {
cmd.Meta
}
// Synopsis returns a one-line synopsis.
func (c *CatchAllUnsetCommand) Synopsis() string {
func (c *CmdCatchAllUnset) Synopsis() string {
return "Unset a catchall user."
}
// Help returns long-form help text.
func (c *CatchAllUnsetCommand) Help() string {
func (c *CmdCatchAllUnset) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s domain
%s %s [-n] domain
Description:
%s
@@ -28,6 +29,10 @@ Description:
Required Args:
domain
The domain name.
Optional Args:
-n
Don't update databases.
`,
c.CmdName, c.SubCmdName,
c.Synopsis())
@@ -36,7 +41,13 @@ Required Args:
}
// Run runs the command and returns the exit status.
func (c *CatchAllUnsetCommand) Run(args []string) int {
func (c *CmdCatchAllUnset) 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
@@ -46,24 +57,20 @@ func (c *CatchAllUnsetCommand) Run(args []string) int {
repo, err := mailfull.OpenRepository(".")
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
if err := repo.CatchAllUserUnset(domainName); err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
mailData, err := repo.MailData()
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
return 1
if noCommit {
return 0
}
err = repo.GenerateDatabases(mailData)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
if err = repo.GenerateDatabases(); err != nil {
c.Meta.Errorf("%v\n", err)
return 1
}

View File

@@ -0,0 +1,49 @@
package main
import (
"fmt"
"github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go/cmd"
)
// CmdCommit represents a CmdCommit.
type CmdCommit struct {
cmd.Meta
}
// Synopsis returns a one-line synopsis.
func (c *CmdCommit) Synopsis() string {
return "Create databases from the structure of the MailData directory."
}
// Help returns long-form help text.
func (c *CmdCommit) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s
Description:
%s
`,
c.CmdName, c.SubCmdName,
c.Synopsis())
return txt[1:]
}
// Run runs the command and returns the exit status.
func (c *CmdCommit) Run(args []string) int {
repo, err := mailfull.OpenRepository(".")
if err != nil {
c.Meta.Errorf("%v\n", err)
return 1
}
if err = repo.GenerateDatabases(); err != nil {
c.Meta.Errorf("%v\n", err)
return 1
}
return 0
}

View File

@@ -1,26 +1,27 @@
package command
package main
import (
"fmt"
"github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go/cmd"
)
// DomainAddCommand represents a DomainAddCommand.
type DomainAddCommand struct {
Meta
// CmdDomainAdd represents a CmdDomainAdd.
type CmdDomainAdd struct {
cmd.Meta
}
// Synopsis returns a one-line synopsis.
func (c *DomainAddCommand) Synopsis() string {
func (c *CmdDomainAdd) Synopsis() string {
return "Create a new domain and postmaster."
}
// Help returns long-form help text.
func (c *DomainAddCommand) Help() string {
func (c *CmdDomainAdd) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s domain
%s %s [-n] domain
Description:
%s
@@ -28,6 +29,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())
@@ -36,7 +41,13 @@ Required Args:
}
// Run runs the command and returns the exit status.
func (c *DomainAddCommand) Run(args []string) int {
func (c *CmdDomainAdd) 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
@@ -46,41 +57,37 @@ func (c *DomainAddCommand) Run(args []string) int {
repo, err := mailfull.OpenRepository(".")
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
domain, err := mailfull.NewDomain(domainName)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
if err := repo.DomainCreate(domain); err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
user, err := mailfull.NewUser("postmaster", mailfull.NeverMatchHashedPassword, nil)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
if err := repo.UserCreate(domainName, user); err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
mailData, err := repo.MailData()
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
return 1
if noCommit {
return 0
}
err = repo.GenerateDatabases(mailData)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
if err = repo.GenerateDatabases(); err != nil {
c.Meta.Errorf("%v\n", err)
return 1
}

View File

@@ -1,26 +1,27 @@
package command
package main
import (
"fmt"
"github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go/cmd"
)
// DomainDelCommand represents a DomainDelCommand.
type DomainDelCommand struct {
Meta
// CmdDomainDel represents a CmdDomainDel.
type CmdDomainDel struct {
cmd.Meta
}
// Synopsis returns a one-line synopsis.
func (c *DomainDelCommand) Synopsis() string {
func (c *CmdDomainDel) Synopsis() string {
return "Delete and backup a domain."
}
// Help returns long-form help text.
func (c *DomainDelCommand) Help() string {
func (c *CmdDomainDel) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s domain
%s %s [-n] domain
Description:
%s
@@ -28,6 +29,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())
@@ -36,7 +41,13 @@ Required Args:
}
// Run runs the command and returns the exit status.
func (c *DomainDelCommand) Run(args []string) int {
func (c *CmdDomainDel) 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
@@ -46,24 +57,20 @@ func (c *DomainDelCommand) Run(args []string) int {
repo, err := mailfull.OpenRepository(".")
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
if err := repo.DomainRemove(domainName); err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
mailData, err := repo.MailData()
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
return 1
if noCommit {
return 0
}
err = repo.GenerateDatabases(mailData)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
if err = repo.GenerateDatabases(); err != nil {
c.Meta.Errorf("%v\n", err)
return 1
}

View File

@@ -0,0 +1,90 @@
package main
import (
"fmt"
"github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go/cmd"
)
// CmdDomainDisable represents a CmdDomainDisable.
type CmdDomainDisable struct {
cmd.Meta
}
// Synopsis returns a one-line synopsis.
func (c *CmdDomainDisable) Synopsis() string {
return "Disable a domain temporarily."
}
// Help returns long-form help text.
func (c *CmdDomainDisable) 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 *CmdDomainDisable) 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 {
c.Meta.Errorf("%v\n", err)
return 1
}
domain, err := repo.Domain(domainName)
if err != nil {
c.Meta.Errorf("%v\n", err)
return 1
}
if domain == nil {
c.Meta.Errorf("%v\n", mailfull.ErrDomainNotExist)
return 1
}
domain.SetDisabled(true)
if err := repo.DomainUpdate(domain); err != nil {
c.Meta.Errorf("%v\n", err)
return 1
}
if noCommit {
return 0
}
if err = repo.GenerateDatabases(); err != nil {
c.Meta.Errorf("%v\n", err)
return 1
}
return 0
}

View File

@@ -0,0 +1,90 @@
package main
import (
"fmt"
"github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go/cmd"
)
// CmdDomainEnable represents a CmdDomainEnable.
type CmdDomainEnable struct {
cmd.Meta
}
// Synopsis returns a one-line synopsis.
func (c *CmdDomainEnable) Synopsis() string {
return "Enable a domain."
}
// Help returns long-form help text.
func (c *CmdDomainEnable) 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 *CmdDomainEnable) 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 {
c.Meta.Errorf("%v\n", err)
return 1
}
domain, err := repo.Domain(domainName)
if err != nil {
c.Meta.Errorf("%v\n", err)
return 1
}
if domain == nil {
c.Meta.Errorf("%v\n", mailfull.ErrDomainNotExist)
return 1
}
domain.SetDisabled(false)
if err := repo.DomainUpdate(domain); err != nil {
c.Meta.Errorf("%v\n", err)
return 1
}
if noCommit {
return 0
}
if err = repo.GenerateDatabases(); err != nil {
c.Meta.Errorf("%v\n", err)
return 1
}
return 0
}

View File

@@ -0,0 +1,62 @@
package main
import (
"fmt"
"sort"
"github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go/cmd"
)
// CmdDomains represents a CmdDomains.
type CmdDomains struct {
cmd.Meta
}
// Synopsis returns a one-line synopsis.
func (c *CmdDomains) Synopsis() string {
return "Show domains."
}
// Help returns long-form help text.
func (c *CmdDomains) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s
Description:
%s
Disabled domains are marked "!" the beginning.
`,
c.CmdName, c.SubCmdName,
c.Synopsis())
return txt[1:]
}
// Run runs the command and returns the exit status.
func (c *CmdDomains) Run(args []string) int {
repo, err := mailfull.OpenRepository(".")
if err != nil {
c.Meta.Errorf("%v\n", err)
return 1
}
domains, err := repo.Domains()
if err != nil {
c.Meta.Errorf("%v\n", err)
return 1
}
sort.Slice(domains, func(i, j int) bool { return domains[i].Name() < domains[j].Name() })
for _, domain := range domains {
disableStr := ""
if domain.Disabled() {
disableStr = "!"
}
fmt.Fprintf(c.UI.Writer, "%s%s\n", disableStr, domain.Name())
}
return 0
}

View File

@@ -1,23 +1,24 @@
package command
package main
import (
"fmt"
"github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go/cmd"
)
// GenConfigCommand represents a GenConfigCommand.
type GenConfigCommand struct {
Meta
// CmdGenConfig represents a CmdGenConfig.
type CmdGenConfig struct {
cmd.Meta
}
// Synopsis returns a one-line synopsis.
func (c *GenConfigCommand) Synopsis() string {
func (c *CmdGenConfig) Synopsis() string {
return "Write a Postfix or Dovecot configuration to stdout."
}
// Help returns long-form help text.
func (c *GenConfigCommand) Help() string {
func (c *CmdGenConfig) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s name
@@ -37,7 +38,7 @@ Required Args:
}
// Run runs the command and returns the exit status.
func (c *GenConfigCommand) Run(args []string) int {
func (c *CmdGenConfig) Run(args []string) int {
if len(args) != 1 {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
@@ -47,7 +48,7 @@ func (c *GenConfigCommand) Run(args []string) int {
repo, err := mailfull.OpenRepository(".")
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
@@ -59,7 +60,7 @@ func (c *GenConfigCommand) Run(args []string) int {
fmt.Fprintf(c.UI.Writer, "%s", repo.GenerateConfigDovecot())
default:
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] Specify \"postfix\" or \"dovecot\".\n")
c.Meta.Errorf("Specify \"postfix\" or \"dovecot\".\n")
return 1
}

View File

@@ -1,23 +1,24 @@
package command
package main
import (
"fmt"
"github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go/cmd"
)
// InitCommand represents a InitCommand.
type InitCommand struct {
Meta
// CmdInit represents a CmdInit.
type CmdInit struct {
cmd.Meta
}
// Synopsis returns a one-line synopsis.
func (c *InitCommand) Synopsis() string {
func (c *CmdInit) Synopsis() string {
return "Initializes current directory as a Mailfull repository."
}
// Help returns long-form help text.
func (c *InitCommand) Help() string {
func (c *CmdInit) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s
@@ -32,9 +33,9 @@ Description:
}
// Run runs the command and returns the exit status.
func (c *InitCommand) Run(args []string) int {
func (c *CmdInit) Run(args []string) int {
if err := mailfull.InitRepository("."); err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}

View File

@@ -1,27 +1,28 @@
package command
package main
import (
"fmt"
"strings"
"github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go/cmd"
)
// UserAddCommand represents a UserAddCommand.
type UserAddCommand struct {
Meta
// CmdUserAdd represents a CmdUserAdd.
type CmdUserAdd struct {
cmd.Meta
}
// Synopsis returns a one-line synopsis.
func (c *UserAddCommand) Synopsis() string {
func (c *CmdUserAdd) Synopsis() string {
return "Create a new user."
}
// Help returns long-form help text.
func (c *UserAddCommand) Help() string {
func (c *CmdUserAdd) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s address
%s %s [-n] address
Description:
%s
@@ -29,6 +30,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())
@@ -37,7 +42,13 @@ Required Args:
}
// Run runs the command and returns the exit status.
func (c *UserAddCommand) Run(args []string) int {
func (c *CmdUserAdd) 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,30 +66,26 @@ func (c *UserAddCommand) Run(args []string) int {
repo, err := mailfull.OpenRepository(".")
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
user, err := mailfull.NewUser(userName, mailfull.NeverMatchHashedPassword, nil)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
if err := repo.UserCreate(domainName, user); err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
mailData, err := repo.MailData()
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
return 1
if noCommit {
return 0
}
err = repo.GenerateDatabases(mailData)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
if err = repo.GenerateDatabases(); err != nil {
c.Meta.Errorf("%v\n", err)
return 1
}

View File

@@ -1,25 +1,26 @@
package command
package main
import (
"fmt"
"strings"
"github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go/cmd"
"github.com/jsimonetti/pwscheme/ssha"
)
// UserCheckPwCommand represents a UserCheckPwCommand.
type UserCheckPwCommand struct {
Meta
// CmdUserCheckPw represents a CmdUserCheckPw.
type CmdUserCheckPw struct {
cmd.Meta
}
// Synopsis returns a one-line synopsis.
func (c *UserCheckPwCommand) Synopsis() string {
func (c *CmdUserCheckPw) Synopsis() string {
return "Check user's password."
}
// Help returns long-form help text.
func (c *UserCheckPwCommand) Help() string {
func (c *CmdUserCheckPw) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s address [password]
@@ -34,7 +35,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())
@@ -43,7 +44,7 @@ Optional Args:
}
// Run runs the command and returns the exit status.
func (c *UserCheckPwCommand) Run(args []string) int {
func (c *CmdUserCheckPw) Run(args []string) int {
if len(args) != 1 && len(args) != 2 {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
@@ -66,24 +67,24 @@ func (c *UserCheckPwCommand) Run(args []string) int {
repo, err := mailfull.OpenRepository(".")
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
user, err := repo.User(domainName, userName)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
if user == nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", mailfull.ErrUserNotExist)
c.Meta.Errorf("%v\n", mailfull.ErrUserNotExist)
return 1
}
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 {
c.Meta.Errorf("%v\n", err)
return 1
}

View File

@@ -1,27 +1,28 @@
package command
package main
import (
"fmt"
"strings"
"github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go/cmd"
)
// UserDelCommand represents a UserDelCommand.
type UserDelCommand struct {
Meta
// CmdUserDel represents a CmdUserDel.
type CmdUserDel struct {
cmd.Meta
}
// Synopsis returns a one-line synopsis.
func (c *UserDelCommand) Synopsis() string {
func (c *CmdUserDel) Synopsis() string {
return "Delete and backup a user."
}
// Help returns long-form help text.
func (c *UserDelCommand) Help() string {
func (c *CmdUserDel) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s address
%s %s [-n] address
Description:
%s
@@ -29,6 +30,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())
@@ -37,7 +42,13 @@ Required Args:
}
// Run runs the command and returns the exit status.
func (c *UserDelCommand) Run(args []string) int {
func (c *CmdUserDel) 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,24 +66,25 @@ func (c *UserDelCommand) Run(args []string) int {
repo, err := mailfull.OpenRepository(".")
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
if userName == "postmaster" {
c.Meta.Errorf("Cannot delete postmaster.\n")
return 1
}
if err := repo.UserRemove(domainName, userName); err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
mailData, err := repo.MailData()
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
return 1
if noCommit {
return 0
}
err = repo.GenerateDatabases(mailData)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
if err = repo.GenerateDatabases(); err != nil {
c.Meta.Errorf("%v\n", err)
return 1
}

View File

@@ -1,28 +1,29 @@
package command
package main
import (
"fmt"
"strings"
"github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go/cmd"
"github.com/jsimonetti/pwscheme/ssha"
)
// UserPasswdCommand represents a UserPasswdCommand.
type UserPasswdCommand struct {
Meta
// CmdUserPasswd represents a CmdUserPasswd.
type CmdUserPasswd struct {
cmd.Meta
}
// Synopsis returns a one-line synopsis.
func (c *UserPasswdCommand) Synopsis() string {
func (c *CmdUserPasswd) Synopsis() string {
return "Update user's password."
}
// Help returns long-form help text.
func (c *UserPasswdCommand) Help() string {
func (c *CmdUserPasswd) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s address [password]
%s %s [-n] address [password]
Description:
%s
@@ -32,9 +33,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())
@@ -43,7 +46,13 @@ Optional Args:
}
// Run runs the command and returns the exit status.
func (c *UserPasswdCommand) Run(args []string) int {
func (c *CmdUserPasswd) 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
@@ -66,33 +75,33 @@ func (c *UserPasswdCommand) Run(args []string) int {
repo, err := mailfull.OpenRepository(".")
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
user, err := repo.User(domainName, userName)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
if user == nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", mailfull.ErrUserNotExist)
c.Meta.Errorf("%v\n", mailfull.ErrUserNotExist)
return 1
}
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 {
c.Meta.Errorf("%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 {
c.Meta.Errorf("%v\n", err)
return 1
}
if input1 != input2 {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] inputs do not match.\n")
c.Meta.Errorf("inputs do not match.\n")
return 1
}
rawPassword = input1
@@ -100,9 +109,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 {
c.Meta.Errorf("%v\n", err)
return 1
}
hashedPassword = str
@@ -111,19 +120,15 @@ func (c *UserPasswdCommand) Run(args []string) int {
user.SetHashedPassword(hashedPassword)
if err := repo.UserUpdate(domainName, user); err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
mailData, err := repo.MailData()
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
return 1
if noCommit {
return 0
}
err = repo.GenerateDatabases(mailData)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
if err = repo.GenerateDatabases(); err != nil {
c.Meta.Errorf("%v\n", err)
return 1
}

View File

@@ -1,24 +1,25 @@
package command
package main
import (
"fmt"
"sort"
"github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go/cmd"
)
// UsersCommand represents a UsersCommand.
type UsersCommand struct {
Meta
// CmdUsers represents a CmdUsers.
type CmdUsers struct {
cmd.Meta
}
// Synopsis returns a one-line synopsis.
func (c *UsersCommand) Synopsis() string {
func (c *CmdUsers) Synopsis() string {
return "Show users."
}
// Help returns long-form help text.
func (c *UsersCommand) Help() string {
func (c *CmdUsers) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s domain
@@ -37,7 +38,7 @@ Required Args:
}
// Run runs the command and returns the exit status.
func (c *UsersCommand) Run(args []string) int {
func (c *CmdUsers) Run(args []string) int {
if len(args) != 1 {
fmt.Fprintf(c.UI.ErrorWriter, "%v\n", c.Help())
return 1
@@ -47,16 +48,16 @@ func (c *UsersCommand) Run(args []string) int {
repo, err := mailfull.OpenRepository(".")
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
users, err := repo.Users(targetDomainName)
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
c.Meta.Errorf("%v\n", err)
return 1
}
sort.Sort(mailfull.UserSlice(users))
sort.Slice(users, func(i, j int) bool { return users[i].Name() < users[j].Name() })
for _, user := range users {
fmt.Fprintf(c.UI.Writer, "%s\n", user.Name())

View File

@@ -1,55 +0,0 @@
package command
import (
"fmt"
"github.com/directorz/mailfull-go"
)
// CommitCommand represents a CommitCommand.
type CommitCommand struct {
Meta
}
// Synopsis returns a one-line synopsis.
func (c *CommitCommand) Synopsis() string {
return "Create databases from the structure of the MailData directory."
}
// Help returns long-form help text.
func (c *CommitCommand) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s
Description:
%s
`,
c.CmdName, c.SubCmdName,
c.Synopsis())
return txt[1:]
}
// Run runs the command and returns the exit status.
func (c *CommitCommand) Run(args []string) int {
repo, err := mailfull.OpenRepository(".")
if 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
}

View File

@@ -1,55 +0,0 @@
package command
import (
"fmt"
"sort"
"github.com/directorz/mailfull-go"
)
// DomainsCommand represents a DomainsCommand.
type DomainsCommand struct {
Meta
}
// Synopsis returns a one-line synopsis.
func (c *DomainsCommand) Synopsis() string {
return "Show domains."
}
// Help returns long-form help text.
func (c *DomainsCommand) Help() string {
txt := fmt.Sprintf(`
Usage:
%s %s
Description:
%s
`,
c.CmdName, c.SubCmdName,
c.Synopsis())
return txt[1:]
}
// Run runs the command and returns the exit status.
func (c *DomainsCommand) Run(args []string) int {
repo, err := mailfull.OpenRepository(".")
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
return 1
}
domains, err := repo.Domains()
if err != nil {
fmt.Fprintf(c.UI.ErrorWriter, "[ERR] %v\n", err)
return 1
}
sort.Sort(mailfull.DomainSlice(domains))
for _, domain := range domains {
fmt.Fprintf(c.UI.Writer, "%s\n", domain.Name())
}
return 0
}

View File

@@ -1,13 +0,0 @@
package command
import (
"github.com/mitchellh/cli"
)
// Meta is for `*Command` struct.
type Meta struct {
UI *cli.BasicUi
CmdName string
SubCmdName string
Version string
}

View File

@@ -4,12 +4,14 @@ Command mailfull is a CLI application using the mailfull package.
package main
import (
"bytes"
"flag"
"fmt"
"os"
"path/filepath"
"github.com/directorz/mailfull-go"
"github.com/directorz/mailfull-go/cmd/mailfull/command"
"github.com/directorz/mailfull-go/cmd"
"github.com/mitchellh/cli"
)
@@ -31,7 +33,7 @@ func main() {
Args: os.Args[1:],
}
meta := command.Meta{
meta := cmd.Meta{
UI: &cli.BasicUi{
Reader: os.Stdin,
Writer: os.Stdout,
@@ -44,87 +46,95 @@ func main() {
c.Commands = map[string]cli.CommandFactory{
"init": func() (cli.Command, error) {
meta.SubCmdName = c.Subcommand()
return &command.InitCommand{Meta: meta}, nil
return &CmdInit{Meta: meta}, nil
},
"genconfig": func() (cli.Command, error) {
meta.SubCmdName = c.Subcommand()
return &command.GenConfigCommand{Meta: meta}, nil
return &CmdGenConfig{Meta: meta}, nil
},
"domains": func() (cli.Command, error) {
meta.SubCmdName = c.Subcommand()
return &command.DomainsCommand{Meta: meta}, nil
return &CmdDomains{Meta: meta}, nil
},
"domainadd": func() (cli.Command, error) {
meta.SubCmdName = c.Subcommand()
return &command.DomainAddCommand{Meta: meta}, nil
return &CmdDomainAdd{Meta: meta}, nil
},
"domaindel": func() (cli.Command, error) {
meta.SubCmdName = c.Subcommand()
return &command.DomainDelCommand{Meta: meta}, nil
return &CmdDomainDel{Meta: meta}, nil
},
"domaindisable": func() (cli.Command, error) {
meta.SubCmdName = c.Subcommand()
return &CmdDomainDisable{Meta: meta}, nil
},
"domainenable": func() (cli.Command, error) {
meta.SubCmdName = c.Subcommand()
return &CmdDomainEnable{Meta: meta}, nil
},
"aliasdomains": func() (cli.Command, error) {
meta.SubCmdName = c.Subcommand()
return &command.AliasDomainsCommand{Meta: meta}, nil
return &CmdAliasDomains{Meta: meta}, nil
},
"aliasdomainadd": func() (cli.Command, error) {
meta.SubCmdName = c.Subcommand()
return &command.AliasDomainAddCommand{Meta: meta}, nil
return &CmdAliasDomainAdd{Meta: meta}, nil
},
"aliasdomaindel": func() (cli.Command, error) {
meta.SubCmdName = c.Subcommand()
return &command.AliasDomainDelCommand{Meta: meta}, nil
return &CmdAliasDomainDel{Meta: meta}, nil
},
"users": func() (cli.Command, error) {
meta.SubCmdName = c.Subcommand()
return &command.UsersCommand{Meta: meta}, nil
return &CmdUsers{Meta: meta}, nil
},
"useradd": func() (cli.Command, error) {
meta.SubCmdName = c.Subcommand()
return &command.UserAddCommand{Meta: meta}, nil
return &CmdUserAdd{Meta: meta}, nil
},
"userdel": func() (cli.Command, error) {
meta.SubCmdName = c.Subcommand()
return &command.UserDelCommand{Meta: meta}, nil
return &CmdUserDel{Meta: meta}, nil
},
"userpasswd": func() (cli.Command, error) {
meta.SubCmdName = c.Subcommand()
return &command.UserPasswdCommand{Meta: meta}, nil
return &CmdUserPasswd{Meta: meta}, nil
},
"usercheckpw": func() (cli.Command, error) {
meta.SubCmdName = c.Subcommand()
return &command.UserCheckPwCommand{Meta: meta}, nil
return &CmdUserCheckPw{Meta: meta}, nil
},
"aliasusers": func() (cli.Command, error) {
meta.SubCmdName = c.Subcommand()
return &command.AliasUsersCommand{Meta: meta}, nil
return &CmdAliasUsers{Meta: meta}, nil
},
"aliasuseradd": func() (cli.Command, error) {
meta.SubCmdName = c.Subcommand()
return &command.AliasUserAddCommand{Meta: meta}, nil
return &CmdAliasUserAdd{Meta: meta}, nil
},
"aliasusermod": func() (cli.Command, error) {
meta.SubCmdName = c.Subcommand()
return &command.AliasUserModCommand{Meta: meta}, nil
return &CmdAliasUserMod{Meta: meta}, nil
},
"aliasuserdel": func() (cli.Command, error) {
meta.SubCmdName = c.Subcommand()
return &command.AliasUserDelCommand{Meta: meta}, nil
return &CmdAliasUserDel{Meta: meta}, nil
},
"catchall": func() (cli.Command, error) {
meta.SubCmdName = c.Subcommand()
return &command.CatchAllCommand{Meta: meta}, nil
return &CmdCatchAll{Meta: meta}, nil
},
"catchallset": func() (cli.Command, error) {
meta.SubCmdName = c.Subcommand()
return &command.CatchAllSetCommand{Meta: meta}, nil
return &CmdCatchAllSet{Meta: meta}, nil
},
"catchallunset": func() (cli.Command, error) {
meta.SubCmdName = c.Subcommand()
return &command.CatchAllUnsetCommand{Meta: meta}, nil
return &CmdCatchAllUnset{Meta: meta}, nil
},
"commit": func() (cli.Command, error) {
meta.SubCmdName = c.Subcommand()
return &command.CommitCommand{Meta: meta}, nil
return &CmdCommit{Meta: meta}, nil
},
}
@@ -135,3 +145,17 @@ func main() {
os.Exit(exitCode)
}
// 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
}

20
cmd/meta.go Normal file
View File

@@ -0,0 +1,20 @@
package cmd
import (
"fmt"
"github.com/mitchellh/cli"
)
// Meta contains options to execute a command.
type Meta struct {
UI *cli.BasicUi
CmdName string
SubCmdName string
Version string
}
// Errorf prints the error to ErrorWriter with the prefix string.
func (m Meta) Errorf(format string, v ...interface{}) {
fmt.Fprintf(m.UI.ErrorWriter, "[ERR] "+format, v...)
}

View File

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

View File

@@ -9,33 +9,84 @@ import (
"strings"
)
// GenerateDatabases generates databases from the MailData directory.
func (r *Repository) GenerateDatabases(md *MailData) error {
sort.Sort(DomainSlice(md.Domains))
sort.Sort(AliasDomainSlice(md.AliasDomains))
// repoData represents a repoData.
type repoData struct {
Domains []*Domain
AliasDomains []*AliasDomain
}
for _, domain := range md.Domains {
sort.Sort(UserSlice(domain.Users))
sort.Sort(AliasUserSlice(domain.AliasUsers))
// repoData returns a repoData.
func (r *Repository) repoData() (*repoData, error) {
domains, err := r.Domains()
if err != nil {
return nil, err
}
aliasDomains, err := r.AliasDomains()
if err != nil {
return nil, err
}
for _, domain := range domains {
users, err := r.Users(domain.Name())
if err != nil {
return nil, err
}
domain.Users = users
aliasUsers, err := r.AliasUsers(domain.Name())
if err != nil {
return nil, err
}
domain.AliasUsers = aliasUsers
catchAllUser, err := r.CatchAllUser(domain.Name())
if err != nil {
return nil, err
}
domain.CatchAllUser = catchAllUser
}
rd := &repoData{
Domains: domains,
AliasDomains: aliasDomains,
}
return rd, nil
}
// GenerateDatabases generates databases from the Repository.
func (r *Repository) GenerateDatabases() error {
rd, err := r.repoData()
if err != nil {
return err
}
sort.Slice(rd.Domains, func(i, j int) bool { return rd.Domains[i].Name() < rd.Domains[j].Name() })
sort.Slice(rd.AliasDomains, func(i, j int) bool { return rd.AliasDomains[i].Name() < rd.AliasDomains[j].Name() })
for _, domain := range rd.Domains {
sort.Slice(domain.Users, func(i, j int) bool { return domain.Users[i].Name() < domain.Users[j].Name() })
sort.Slice(domain.AliasUsers, func(i, j int) bool { return domain.AliasUsers[i].Name() < domain.AliasUsers[j].Name() })
}
// Generate files
if err := r.generateDbDomains(md); err != nil {
if err := r.generateDbDomains(rd); err != nil {
return err
}
if err := r.generateDbDestinations(md); err != nil {
if err := r.generateDbDestinations(rd); err != nil {
return err
}
if err := r.generateDbMaildirs(md); err != nil {
if err := r.generateDbMaildirs(rd); err != nil {
return err
}
if err := r.generateDbLocaltable(md); err != nil {
if err := r.generateDbLocaltable(rd); err != nil {
return err
}
if err := r.generateDbForwards(md); err != nil {
if err := r.generateDbForwards(rd); err != nil {
return err
}
if err := r.generateDbPasswords(md); err != nil {
if err := r.generateDbPasswords(rd); err != nil {
return err
}
@@ -59,7 +110,7 @@ func (r *Repository) GenerateDatabases(md *MailData) error {
return nil
}
func (r *Repository) generateDbDomains(md *MailData) error {
func (r *Repository) generateDbDomains(rd *repoData) error {
dbDomains, err := os.Create(filepath.Join(r.DirDatabasePath, FileNameDbDomains))
if err != nil {
return err
@@ -69,13 +120,17 @@ func (r *Repository) generateDbDomains(md *MailData) error {
}
defer dbDomains.Close()
for _, domain := range md.Domains {
for _, domain := range rd.Domains {
if domain.Disabled() {
continue
}
if _, err := fmt.Fprintf(dbDomains, "%s virtual\n", domain.Name()); err != nil {
return err
}
}
for _, aliasDomain := range md.AliasDomains {
for _, aliasDomain := range rd.AliasDomains {
if _, err := fmt.Fprintf(dbDomains, "%s virtual\n", aliasDomain.Name()); err != nil {
return err
}
@@ -84,7 +139,7 @@ func (r *Repository) generateDbDomains(md *MailData) error {
return nil
}
func (r *Repository) generateDbDestinations(md *MailData) error {
func (r *Repository) generateDbDestinations(rd *repoData) error {
dbDestinations, err := os.Create(filepath.Join(r.DirDatabasePath, FileNameDbDestinations))
if err != nil {
return err
@@ -94,7 +149,11 @@ func (r *Repository) generateDbDestinations(md *MailData) error {
}
defer dbDestinations.Close()
for _, domain := range md.Domains {
for _, domain := range rd.Domains {
if domain.Disabled() {
continue
}
// ho-ge.example.com -> ho_ge.example.com
underscoredDomainName := domain.Name()
underscoredDomainName = strings.Replace(underscoredDomainName, `-`, `_`, -1)
@@ -115,7 +174,7 @@ func (r *Repository) generateDbDestinations(md *MailData) error {
}
}
for _, aliasDomain := range md.AliasDomains {
for _, aliasDomain := range rd.AliasDomains {
if aliasDomain.Target() == domain.Name() {
if _, err := fmt.Fprintf(dbDestinations, "%s@%s %s@%s\n", userName, aliasDomain.Name(), user.Name(), domain.Name()); err != nil {
return err
@@ -129,7 +188,7 @@ func (r *Repository) generateDbDestinations(md *MailData) error {
return err
}
for _, aliasDomain := range md.AliasDomains {
for _, aliasDomain := range rd.AliasDomains {
if aliasDomain.Target() == domain.Name() {
if _, err := fmt.Fprintf(dbDestinations, "%s@%s %s@%s\n", aliasUser.Name(), aliasDomain.Name(), aliasUser.Name(), domain.Name()); err != nil {
return err
@@ -142,7 +201,7 @@ func (r *Repository) generateDbDestinations(md *MailData) error {
return nil
}
func (r *Repository) generateDbMaildirs(md *MailData) error {
func (r *Repository) generateDbMaildirs(rd *repoData) error {
dbMaildirs, err := os.Create(filepath.Join(r.DirDatabasePath, FileNameDbMaildirs))
if err != nil {
return err
@@ -152,7 +211,11 @@ func (r *Repository) generateDbMaildirs(md *MailData) error {
}
defer dbMaildirs.Close()
for _, domain := range md.Domains {
for _, domain := range rd.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
@@ -163,7 +226,7 @@ func (r *Repository) generateDbMaildirs(md *MailData) error {
return nil
}
func (r *Repository) generateDbLocaltable(md *MailData) error {
func (r *Repository) generateDbLocaltable(rd *repoData) error {
dbLocaltable, err := os.Create(filepath.Join(r.DirDatabasePath, FileNameDbLocaltable))
if err != nil {
return err
@@ -173,7 +236,11 @@ func (r *Repository) generateDbLocaltable(md *MailData) error {
}
defer dbLocaltable.Close()
for _, domain := range md.Domains {
for _, domain := range rd.Domains {
if domain.Disabled() {
continue
}
// ho-ge.example.com -> ho_ge\.example\.com
escapedDomainName := domain.Name()
escapedDomainName = strings.Replace(escapedDomainName, `-`, `_`, -1)
@@ -187,7 +254,7 @@ func (r *Repository) generateDbLocaltable(md *MailData) error {
return nil
}
func (r *Repository) generateDbForwards(md *MailData) error {
func (r *Repository) generateDbForwards(rd *repoData) error {
dbForwards, err := os.Create(filepath.Join(r.DirDatabasePath, FileNameDbForwards))
if err != nil {
return err
@@ -197,7 +264,11 @@ func (r *Repository) generateDbForwards(md *MailData) error {
}
defer dbForwards.Close()
for _, domain := range md.Domains {
for _, domain := range rd.Domains {
if domain.Disabled() {
continue
}
// ho-ge.example.com -> ho_ge.example.com
underscoredDomainName := domain.Name()
underscoredDomainName = strings.Replace(underscoredDomainName, `-`, `_`, -1)
@@ -223,7 +294,7 @@ func (r *Repository) generateDbForwards(md *MailData) error {
return nil
}
func (r *Repository) generateDbPasswords(md *MailData) error {
func (r *Repository) generateDbPasswords(rd *repoData) error {
dbPasswords, err := os.Create(filepath.Join(r.DirDatabasePath, FileNameDbPasswords))
if err != nil {
return err
@@ -233,7 +304,11 @@ func (r *Repository) generateDbPasswords(md *MailData) error {
}
defer dbPasswords.Close()
for _, domain := range md.Domains {
for _, domain := range rd.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
```

128
domain.go
View File

@@ -11,36 +11,49 @@ import (
// Domain represents a Domain.
type Domain struct {
name string
disabled bool
Users []*User
AliasUsers []*AliasUser
CatchAllUser *CatchAllUser
}
// DomainSlice attaches the methods of sort.Interface to []*Domain.
type DomainSlice []*Domain
func (p DomainSlice) Len() int { return len(p) }
func (p DomainSlice) Less(i, j int) bool { return p[i].Name() < p[j].Name() }
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 +75,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 +113,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 +198,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 +253,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

@@ -1,47 +0,0 @@
package mailfull
// MailData represents a MailData.
type MailData struct {
Domains []*Domain
AliasDomains []*AliasDomain
}
// MailData returns a MailData.
func (r *Repository) MailData() (*MailData, error) {
domains, err := r.Domains()
if err != nil {
return nil, err
}
aliasDomains, err := r.AliasDomains()
if err != nil {
return nil, err
}
for _, domain := range domains {
users, err := r.Users(domain.Name())
if err != nil {
return nil, err
}
domain.Users = users
aliasUsers, err := r.AliasUsers(domain.Name())
if err != nil {
return nil, err
}
domain.AliasUsers = aliasUsers
catchAllUser, err := r.CatchAllUser(domain.Name())
if err != nil {
return nil, err
}
domain.CatchAllUser = catchAllUser
}
mailData := &MailData{
Domains: domains,
AliasDomains: aliasDomains,
}
return mailData, nil
}

View File

@@ -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.
@@ -44,8 +45,8 @@ type RepositoryConfig struct {
DirDatabasePath string `toml:"dir_database"`
DirMailDataPath string `toml:"dir_maildata"`
Username string `toml:"username"`
CmdPostalias string `toml:"postalias"`
CmdPostmap string `toml:"postmap"`
CmdPostalias string `toml:"cmd_postalias"`
CmdPostmap string `toml:"cmd_postmap"`
}
// Normalize normalizes paramaters of the RepositoryConfig.
@@ -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

@@ -19,13 +19,6 @@ type User struct {
forwards []string
}
// UserSlice attaches the methods of sort.Interface to []*User.
type UserSlice []*User
func (p UserSlice) Len() int { return len(p) }
func (p UserSlice) Less(i, j int) bool { return p[i].Name() < p[j].Name() }
func (p UserSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
// NewUser creates a new User instance.
func NewUser(name, hashedPassword string, forwards []string) (*User, error) {
u := &User{}

View File

@@ -1,4 +1,4 @@
package mailfull
// Version is a version number.
const Version = "0.0.1"
const Version = "v1.0.0"