Merge pull request 'betterbird: lock the ask to be the default mail client off (hq issue 345)' (#253) from fix/345-no-default-client-check into main
This commit was merged in pull request #253.
This commit is contained in:
@@ -56,7 +56,15 @@ honours) and installs Owl into every profile from `file:///opt/betterbird/owl/ow
|
||||
`force_installed`, with `updates_disabled`. A profile that already runs another Owl keeps it: the policy
|
||||
installs the pinned one where Owl is missing. A newer Owl in the profile (g14 runs 1.5.7) is said as a
|
||||
fact by `betterbird_status`; Owl installed by policy cannot be removed in Betterbird, so the remedy is
|
||||
moving the pin. An older one is a finding of `betterbird_check`, with the same remedy. **Owl's licence stays the person's**: the mesh holds no licence, key or payment (ADR 0316 §9).
|
||||
moving the pin. An older one is a finding of `betterbird_check`, with the same remedy.
|
||||
|
||||
**Betterbird never asks to be the default client.** The policy locks `mail.shell.checkDefaultClient` off
|
||||
(`Preferences`, `Status: locked`): Betterbird's own default (`all-thunderbird.js`) is to ask at every start,
|
||||
with a window to make it the default for mail, news, feeds and calendar files, while that preference is true
|
||||
(`messenger.js`). Betterbird 153 has no policy of its own for it (its `policies-schema.json`), and its
|
||||
`Preferences` policy accepts the `mail.` prefix. A lock, not a default: the window's own "always check" box
|
||||
writes the preference into the profile, which wins over a default; a lock wins over the profile and greys the
|
||||
box out in the settings. `betterbird_check` says when Betterbird would ask (novox/hq issue 345). **Owl's licence stays the person's**: the mesh holds no licence, key or payment (ADR 0316 §9).
|
||||
|
||||
**No start of its own.** Betterbird is started by the operator from the launcher. The start
|
||||
(`/usr/local/bin/betterbird`) first runs `betterbird-tools prepare`, which, with no Betterbird of the
|
||||
@@ -159,7 +167,7 @@ and key.
|
||||
| `betterbird_user_js` (r; a with `apply`) | the declared user.js against the profile's; `apply` places it |
|
||||
| `betterbird_log` (r) | the last lines of the journal of `mesh-betterbird` (or of the reminder test), addresses and credentials masked (the value after `password=`, `token:` and the like, and the token after `Bearer`/`Basic`). A Betterbird the module did not start prints elsewhere, and the answer says where |
|
||||
| `betterbird_restart` (a) | ends Betterbird (asked to quit, forced after 8 s), places the user.js where allowed, starts it as `mesh-betterbird`. **Refused while a composer window is open** (X instance `Msgcompose`, whatever its class), because the unsent message would be lost, unless `discard_unsent` is true, which only the operator's yes justifies. It answers within the runtime's 30 s: a start not yet seen after 4 s is said in `note`, and `betterbird_status` shows it |
|
||||
| `betterbird_check` (r) | installed from the mesh's release and not behind the newest published build; the policy placed; Owl at its pin and active; one running; profile found; the user.js on the machine, valid, placed, in effect; the declared accounts in the list, none refused, each signed in once; its output read |
|
||||
| `betterbird_check` (r) | installed from the mesh's release and not behind the newest published build; the policy placed; no ask at the start to be the default mail client; Owl at its pin and active; one running; profile found; the user.js on the machine, valid, placed, in effect; the declared accounts in the list, none refused, each signed in once; its output read |
|
||||
| `betterbird_accounts` (r; a with `propose`) | the profile's accounts by count and kind (declarable, Owl's, others); with `propose`, the setting `accounts` built from them and proposed to the operator (ADR 0317 §9) |
|
||||
| `betterbird_reminder_test` (a) | a reminder on demand without the operator's calendar (below) |
|
||||
|
||||
|
||||
@@ -849,7 +849,8 @@ type CheckAnswer struct {
|
||||
}
|
||||
|
||||
// Check verifies what the module promises: Betterbird installed, one running in the session, its
|
||||
// profile found, the module's user.js declared, valid, placed and in effect, and its output read.
|
||||
// profile found, the module's user.js declared, valid, placed and in effect, its output read, and no ask at its
|
||||
// start to be the default mail client (novox/hq issue 345).
|
||||
func (m *Machine) Check() (CheckAnswer, error) {
|
||||
a := CheckAnswer{Findings: []Finding{}}
|
||||
add := func(what, do string) { a.Findings = append(a.Findings, Finding{what, do}) }
|
||||
@@ -875,6 +876,10 @@ func (m *Machine) Check() (CheckAnswer, error) {
|
||||
if in.From == "the mesh" && !strings.HasPrefix(in.Policy, "placed:") {
|
||||
add("the enterprise policy is "+in.Policy, "push the module to this node")
|
||||
}
|
||||
if why := m.defaultClientAsk(profilePath); why != "" && in.From != "not installed" {
|
||||
add("Betterbird asks at its start to be the default mail client: "+why,
|
||||
"push the module to this node: its policy locks "+defaultClientPref+" off (novox/hq issue 345)")
|
||||
}
|
||||
switch {
|
||||
case in.From != "the mesh":
|
||||
case in.Owl == nil || in.Owl.Pinned == "":
|
||||
|
||||
@@ -30,6 +30,10 @@ const (
|
||||
// owlFile is Owl's xpi as the mesh placed it, and the URL the policy installs it from.
|
||||
owlFile = installDir + "/owl/owl.xpi"
|
||||
owlID = "owl@beonex.com"
|
||||
// defaultClientPref is Betterbird's ask at its start to be the default mail, news, feed and calendar
|
||||
// client (Thunderbird's all-thunderbird.js: pref("mail.shell.checkDefaultClient", true); messenger.js opens
|
||||
// systemIntegrationDialog.xhtml while it is true). The module's policy locks it off (novox/hq issue 345).
|
||||
defaultClientPref = "mail.shell.checkDefaultClient"
|
||||
// preparation is the tools' program as the module places it for its start (betterbird-tools prepare).
|
||||
preparation = "/usr/local/lib/mesh-betterbird/betterbird-tools"
|
||||
// versionsURL is where Betterbird publishes its newest build per major version: what its own updater
|
||||
@@ -167,7 +171,8 @@ func (m *Machine) policyState() string {
|
||||
if why := policyProblem(raw); why != "" {
|
||||
return "placed, and " + why
|
||||
}
|
||||
return "placed: Betterbird's updater off, Owl installed into every profile from the mesh's xpi, its updates off"
|
||||
return "placed: Betterbird's updater off, Owl installed into every profile from the mesh's xpi, its updates off, " +
|
||||
"and the ask to be the default mail client locked off"
|
||||
}
|
||||
|
||||
// policyProblem is what the policy fails to say of what ADR 0316 asks, or "".
|
||||
@@ -180,6 +185,10 @@ func policyProblem(raw []byte) string {
|
||||
URL string `json:"install_url"`
|
||||
Disabled bool `json:"updates_disabled"`
|
||||
} `json:"ExtensionSettings"`
|
||||
Preferences map[string]struct {
|
||||
Value any `json:"Value"`
|
||||
Status string `json:"Status"`
|
||||
} `json:"Preferences"`
|
||||
} `json:"policies"`
|
||||
}
|
||||
if err := json.Unmarshal(raw, &p); err != nil {
|
||||
@@ -196,9 +205,37 @@ func policyProblem(raw []byte) string {
|
||||
case !owl.Disabled:
|
||||
return "it leaves Owl's updates on"
|
||||
}
|
||||
if pr, ok := p.Policies.Preferences[defaultClientPref]; !ok || pr.Value != false || pr.Status != "locked" {
|
||||
return "it does not lock Betterbird's ask to be the default mail client off (Preferences " + defaultClientPref + ": false, locked)"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// defaultClientAsk says why Betterbird asks at its start to be the default mail client, or "" when it does
|
||||
// not: the module's policy locks the ask off, else the profile's user.js (which wins at the start) or prefs.js
|
||||
// turns it off. Betterbird's own default is to ask.
|
||||
func (m *Machine) defaultClientAsk(profile string) string {
|
||||
raw, err := readBounded(m.path(policyFile))
|
||||
if err == nil && !strings.Contains(policyProblem(raw), defaultClientPref) {
|
||||
return ""
|
||||
}
|
||||
if profile != "" {
|
||||
for _, f := range []string{"user.js", "prefs.js"} {
|
||||
b, err := readBounded(filepath.Join(profile, f))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if v, ok := readPrefs(b)[defaultClientPref]; ok {
|
||||
if v == "false" {
|
||||
return ""
|
||||
}
|
||||
return "the profile's " + f + " sets " + defaultClientPref + " to " + v + " and no policy locks it off"
|
||||
}
|
||||
}
|
||||
}
|
||||
return "no policy locks " + defaultClientPref + " off and Betterbird's default is to ask"
|
||||
}
|
||||
|
||||
// xpiVersion is the version an add-on's manifest.json says, or "" when it cannot be read.
|
||||
func xpiVersion(path string) string {
|
||||
r, err := zip.OpenReader(path)
|
||||
|
||||
@@ -172,3 +172,60 @@ func TestTheUserJSDescriptionSaysAdoption(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Betterbird never asks at its start to be the default mail client: the module's policy locks
|
||||
// mail.shell.checkDefaultClient off, and the check says so when it is on (novox/hq issue 345).
|
||||
func TestTheDefaultClientAskIsLockedOffAndTheCheckSaysWhenItIsOn(t *testing.T) {
|
||||
f := newFake(t)
|
||||
f.betterbird()
|
||||
f.placedByTheMesh()
|
||||
links(t, nil)
|
||||
asks := func() string {
|
||||
t.Helper()
|
||||
c, err := f.Check()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, x := range c.Findings {
|
||||
if strings.Contains(x.What, "asks at its start") {
|
||||
return x.What + " | " + x.Do
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
if got := asks(); got != "" {
|
||||
t.Fatalf("the module's policy locks the ask off, yet: %s", got)
|
||||
}
|
||||
unlocked := `{"policies": {"DisableAppUpdate": true, "ExtensionSettings": {"owl@beonex.com": {"installation_mode": "force_installed",
|
||||
"install_url": "file:///opt/betterbird/owl/owl.xpi", "updates_disabled": true}}}}`
|
||||
if why := policyProblem([]byte(unlocked)); !strings.Contains(why, defaultClientPref) {
|
||||
t.Fatalf("a policy that leaves the ask on is said: %q", why)
|
||||
}
|
||||
f.write(policyFile, unlocked)
|
||||
if got := asks(); !strings.Contains(got, "default mail client") || !strings.Contains(got, "push the module") {
|
||||
t.Fatalf("the ask on is a finding with its remedy: %q", got)
|
||||
}
|
||||
f.write(policyFile, `{"policies": {"DisableAppUpdate": true, "Preferences": {"mail.shell.checkDefaultClient": {"Value": false, "Status": "default"}},
|
||||
"ExtensionSettings": {"owl@beonex.com": {"installation_mode": "force_installed", "install_url": "file:///opt/betterbird/owl/owl.xpi", "updates_disabled": true}}}}`)
|
||||
if why := policyProblem(mustRead(t, f, policyFile)); !strings.Contains(why, "locked") {
|
||||
t.Fatalf("a default the profile can override is not a lock: %q", why)
|
||||
}
|
||||
f.write(profileDir+"/prefs.js", `user_pref("mail.shell.checkDefaultClient", true);`+"\n")
|
||||
if got := asks(); got == "" {
|
||||
t.Fatal("a profile that turned the ask back on over an unlocked default is a finding")
|
||||
}
|
||||
f.write(policyFile, unlocked)
|
||||
f.write(profileDir+"/prefs.js", `user_pref("mail.shell.checkDefaultClient", false);`+"\n")
|
||||
if got := asks(); got != "" {
|
||||
t.Fatalf("a profile with the ask off does not ask: %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
func mustRead(t *testing.T, f *fake, p string) []byte {
|
||||
t.Helper()
|
||||
raw, err := os.ReadFile(filepath.Join(f.Root, p))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return raw
|
||||
}
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
"install_url": "file:///opt/betterbird/owl/owl.xpi",
|
||||
"updates_disabled": true
|
||||
}
|
||||
},
|
||||
"Preferences": {
|
||||
"mail.shell.checkDefaultClient": {
|
||||
"Value": false,
|
||||
"Status": "locked"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user