blob: d4bced08d7ca9d715ecf24d8e2b68fc083538536 [file] [log] [blame]
giolekva1cf54312021-09-23 21:27:20 +04001package main
2
3import (
4 "bufio"
5 "embed"
6 "flag"
7 "fmt"
8 "html/template"
9 "io"
10 "io/ioutil"
11 "log"
12 "net/http"
13 "os/exec"
14)
15
16var port = flag.Int("port", 8080, "Port to listen on.")
17var maddyConfig = flag.String("maddy-config", "", "Path to the Maddy configuration file.")
18
19//go:embed templates/*
20var tmpls embed.FS
21
22type Templates struct {
23 Index *template.Template
24}
25
26func ParseTemplates(fs embed.FS) (*Templates, error) {
27 index, err := template.ParseFS(fs, "templates/index.html")
28 if err != nil {
29 return nil, err
30 }
31 return &Templates{index}, nil
32}
33
34type MaddyManager struct {
35 configPath string
36}
37
38func (m MaddyManager) ListAccounts() ([]string, error) {
39 cmd := exec.Command("maddyctl", "-config", m.configPath, "creds", "list")
40 stdout, err := cmd.StdoutPipe()
41 if err != nil {
42 return nil, err
43 }
44 if err := cmd.Start(); err != nil {
45 return nil, err
46 }
47 scanner := bufio.NewScanner(stdout)
48 accts := make([]string, 0)
49 for scanner.Scan() {
50 acct := scanner.Text()
51 if len(acct) == 0 {
52 continue
53 }
54 accts = append(accts, acct)
55 }
56 return accts, nil
57
58}
59
60func (m MaddyManager) CreateAccount(username, password string) error {
61 cmd := exec.Command("maddyctl", "-config", m.configPath, "creds", "create", username)
62 stdin, err := cmd.StdinPipe()
63 if err != nil {
64 return err
65 }
66 if err := cmd.Start(); err != nil {
67 return err
68 }
69 go func() {
70 defer stdin.Close()
71 io.WriteString(stdin, password)
72 }()
73 if err := cmd.Wait(); err != nil {
74 return err
75 }
76 // Create IMAP
77 cmd = exec.Command("maddyctl", "-config", m.configPath, "imap-acct", "create", username)
78 return cmd.Run()
79}
80
81type MaddyHandler struct {
82 mgr MaddyManager
83 tmpls *Templates
84}
85
86func (h *MaddyHandler) handleListAccounts(w http.ResponseWriter, r *http.Request) {
87 accts, err := h.mgr.ListAccounts()
88 if err != nil {
89 http.Error(w, err.Error(), http.StatusInternalServerError)
90 return
91 }
92 if err := h.tmpls.Index.Execute(w, accts); err != nil {
93 http.Error(w, err.Error(), http.StatusInternalServerError)
94 }
95}
96
97func (h *MaddyHandler) handleCreateAccount(w http.ResponseWriter, r *http.Request) {
98 if err := r.ParseForm(); err != nil {
99 http.Error(w, err.Error(), http.StatusBadRequest)
100 return
101 }
102
103 err := h.mgr.CreateAccount(r.FormValue("username"), r.FormValue("password"))
104 if err != nil {
105 http.Error(w, err.Error(), http.StatusInternalServerError)
106 return
107 }
108 http.Redirect(w, r, "/", http.StatusSeeOther)
109}
110
111func main() {
112 flag.Parse()
113 t, err := ParseTemplates(tmpls)
114 if err != nil {
115 log.Fatal(err)
116 }
117 mgr := MaddyManager{
118 configPath: *maddyConfig,
119 }
120 handler := MaddyHandler{
121 mgr: mgr,
122 tmpls: t,
123 }
124 http.HandleFunc("/", handler.handleListAccounts)
125 http.HandleFunc("/create", handler.handleCreateAccount)
126 fmt.Printf("Starting HTTP server on port: %d\n", *port)
127 fmt.Printf("Maddy config: %s\n", *maddyConfig)
128 if cfg, err := ioutil.ReadFile(*maddyConfig); err != nil {
129 log.Fatal(err)
130 } else {
131 log.Print(string(cfg))
132 }
133 log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), nil))
134
135}