internal/control/adminmail.go
93 lines · 3054 bytes
1package control
2
3import (
4 "fmt"
5 "io"
6 "time"
7
8 "gitbay.org/gitbay/internal/imapc"
9 "gitbay.org/gitbay/internal/protocol"
10)
11
12func init() {
13 register(Command{Path: []string{"admin", "mail", "inbound", "check"},
14 Summary: "connect to the reply mailbox read-only and report what is waiting",
15 Usage: "admin mail inbound check",
16 Examples: []string{"admin mail inbound check"},
17 ReadOnly: true, Run: runAdminMailInboundCheck})
18}
19
20const unauthenticatedWarning = "require_dkim and trusted_authserv_id are unset: a reply's From is not authenticated"
21
22// runAdminMailInboundCheck logs in to the [mail.inbound] mailbox and
23// opens it with EXAMINE, which changes no flag, so a check never marks a
24// reply seen before the poller reads it.
25func runAdminMailInboundCheck(c *Ctx, args []string) int {
26 if code := requireInstanceAdmin(c); code >= 0 {
27 return code
28 }
29 if len(args) != 0 {
30 return c.usage()
31 }
32 in := c.Cfg.Mail.Inbound
33 type out struct {
34 Enabled bool `json:"enabled"`
35 Server string `json:"server,omitempty"`
36 Mailbox string `json:"mailbox,omitempty"`
37 Messages int `json:"messages"`
38 Unseen int `json:"unseen"`
39 // RequireDKIM and TrustedAuthservID are how a reply's From
40 // is authenticated.
41 RequireDKIM bool `json:"require_dkim"`
42 TrustedAuthservID string `json:"trusted_authserv_id,omitempty"`
43 Warning string `json:"warning,omitempty"`
44 }
45 if !in.Enabled {
46 return c.emitView(out{}, func(w io.Writer) {
47 fmt.Fprintln(w, "inbound mail is off ([mail.inbound] enabled = false)")
48 }, func() screen {
49 return screen{fields: []field{{"Inbound", []cell{cText("off")}}}}
50 })
51 }
52 cl, n, err := imapc.Open(in, true, 30*time.Second)
53 if err != nil {
54 return c.fail(protocol.ExitFailure, "%s: %v", in.Addr(), err)
55 }
56 defer cl.Close()
57 unseen, err := cl.Unseen()
58 if err != nil {
59 return c.fail(protocol.ExitFailure, "%s: %v", in.Addr(), err)
60 }
61 d := out{Enabled: true, Server: in.Addr(), Mailbox: in.MailboxName(), Messages: n, Unseen: len(unseen),
62 RequireDKIM: in.RequireDKIM, TrustedAuthservID: in.TrustedAuthservID}
63 if !in.Authenticated() {
64 d.Warning = unauthenticatedWarning
65 fmt.Fprintln(c.Stderr, "warning: "+d.Warning)
66 }
67 return c.emitView(d, func(w io.Writer) {
68 c.view(w).fields(
69 "server", d.Server,
70 "mailbox", d.Mailbox,
71 "messages", fmt.Sprintf("%d", d.Messages),
72 "unseen", fmt.Sprintf("%d", d.Unseen),
73 "require_dkim", fmt.Sprintf("%t", d.RequireDKIM),
74 "trusted_authserv_id", d.TrustedAuthservID,
75 )
76 }, func() screen {
77 auth := cMark("✓ dkim", sgrGreen)
78 if d.Warning != "" {
79 auth = cMark("✗ "+d.Warning, sgrRed)
80 } else if !d.RequireDKIM {
81 auth = cMeta("dkim not required")
82 }
83 s := screen{fields: []field{
84 {"Inbound", []cell{cText(d.Server), cMeta(d.Mailbox)}},
85 {"Messages", []cell{cText(fmt.Sprint(d.Messages)), cMeta(fmt.Sprintf("%d unseen", d.Unseen))}},
86 {"Auth", []cell{auth}},
87 }}
88 if d.TrustedAuthservID != "" {
89 s.fields = append(s.fields, field{"Authserv", []cell{cText(d.TrustedAuthservID)}})
90 }
91 return s
92 })
93}