internal/control/adminmail.go

v1.42.0
gitbay/internal/control/adminmail.go history · blame · raw

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}