package mailfull import ( "fmt" "os" "os/exec" "path/filepath" "sort" "strings" ) // repoData represents a repoData. type repoData struct { Domains []*Domain AliasDomains []*AliasDomain } // 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(rd); err != nil { return err } if err := r.generateDbDestinations(rd); err != nil { return err } if err := r.generateDbMaildirs(rd); err != nil { return err } if err := r.generateDbLocaltable(rd); err != nil { return err } if err := r.generateDbForwards(rd); err != nil { return err } if err := r.generateDbPasswords(rd); err != nil { return err } // Generate DBs if err := exec.Command(r.CmdPostmap, filepath.Join(r.DirDatabasePath, FileNameDbDomains)).Run(); err != nil { return err } if err := exec.Command(r.CmdPostmap, filepath.Join(r.DirDatabasePath, FileNameDbDestinations)).Run(); err != nil { return err } if err := exec.Command(r.CmdPostmap, filepath.Join(r.DirDatabasePath, FileNameDbMaildirs)).Run(); err != nil { return err } if err := exec.Command(r.CmdPostmap, filepath.Join(r.DirDatabasePath, FileNameDbLocaltable)).Run(); err != nil { return err } if err := exec.Command(r.CmdPostalias, filepath.Join(r.DirDatabasePath, FileNameDbForwards)).Run(); err != nil { return err } return nil } func (r *Repository) generateDbDomains(rd *repoData) error { dbDomains, err := os.Create(filepath.Join(r.DirDatabasePath, FileNameDbDomains)) if err != nil { return err } if err := dbDomains.Chown(r.uid, r.gid); err != nil { return err } defer dbDomains.Close() 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 rd.AliasDomains { if _, err := fmt.Fprintf(dbDomains, "%s virtual\n", aliasDomain.Name()); err != nil { return err } } return nil } func (r *Repository) generateDbDestinations(rd *repoData) error { dbDestinations, err := os.Create(filepath.Join(r.DirDatabasePath, FileNameDbDestinations)) if err != nil { return err } if err := dbDestinations.Chown(r.uid, r.gid); err != nil { return err } defer dbDestinations.Close() 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) for _, user := range domain.Users { userName := user.Name() if cu := domain.CatchAllUser; cu != nil && cu.Name() == user.Name() { userName = "" } if len(user.Forwards()) > 0 { if _, err := fmt.Fprintf(dbDestinations, "%s@%s %s|%s\n", userName, domain.Name(), underscoredDomainName, user.Name()); err != nil { return err } } else { if _, err := fmt.Fprintf(dbDestinations, "%s@%s %s@%s\n", userName, domain.Name(), user.Name(), domain.Name()); err != nil { return err } } 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 } } } } for _, aliasUser := range domain.AliasUsers { if _, err := fmt.Fprintf(dbDestinations, "%s@%s %s\n", aliasUser.Name(), domain.Name(), strings.Join(aliasUser.Targets(), ",")); err != nil { return err } 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 } } } } } return nil } func (r *Repository) generateDbMaildirs(rd *repoData) error { dbMaildirs, err := os.Create(filepath.Join(r.DirDatabasePath, FileNameDbMaildirs)) if err != nil { return err } if err := dbMaildirs.Chown(r.uid, r.gid); err != nil { return err } defer dbMaildirs.Close() 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 } } } return nil } func (r *Repository) generateDbLocaltable(rd *repoData) error { dbLocaltable, err := os.Create(filepath.Join(r.DirDatabasePath, FileNameDbLocaltable)) if err != nil { return err } if err := dbLocaltable.Chown(r.uid, r.gid); err != nil { return err } defer dbLocaltable.Close() 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) escapedDomainName = strings.Replace(escapedDomainName, `.`, `\.`, -1) if _, err := fmt.Fprintf(dbLocaltable, "/^%s\\|.*$/ local\n", escapedDomainName); err != nil { return err } } return nil } func (r *Repository) generateDbForwards(rd *repoData) error { dbForwards, err := os.Create(filepath.Join(r.DirDatabasePath, FileNameDbForwards)) if err != nil { return err } if err := dbForwards.Chown(r.uid, r.gid); err != nil { return err } defer dbForwards.Close() 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) for _, user := range domain.Users { if len(user.Forwards()) > 0 { if _, err := fmt.Fprintf(dbForwards, "%s|%s:%s\n", underscoredDomainName, user.Name(), strings.Join(user.Forwards(), ",")); err != nil { return err } } else { if _, err := fmt.Fprintf(dbForwards, "%s|%s:/dev/null\n", underscoredDomainName, user.Name()); err != nil { return err } } } } // drop real user if _, err := fmt.Fprintf(dbForwards, "%s:/dev/null\n", r.Username); err != nil { return err } return nil } func (r *Repository) generateDbPasswords(rd *repoData) error { dbPasswords, err := os.Create(filepath.Join(r.DirDatabasePath, FileNameDbPasswords)) if err != nil { return err } if err := dbPasswords.Chown(r.uid, r.gid); err != nil { return err } defer dbPasswords.Close() 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 } } } return nil }