telegabber/telegram/omemo_commands.go
2026-07-30 00:09:22 -04:00

129 lines
3.7 KiB
Go

package telegram
import (
"fmt"
"strings"
"dev.narayana.im/narayana/telegabber/e2ee"
"dev.narayana.im/narayana/telegabber/xmpp/gateway"
)
// cmdOmemoChat implements the chat-scoped "/omemo" command: with no
// arguments, reports this chat's current OMEMO state (account-wide mode,
// any override, and the resulting effective state); "on"/"off" forces this
// specific chat regardless of the account-wide mode (persistence.Session.OMEMO,
// via e2ee.Mode/e2ee.ShouldEncrypt); "clear" removes that override, reverting
// to following the account-wide mode; "devices ..." manages this chat's
// peer's device trust (see cmdOmemoDevices).
func (c *Client) cmdOmemoChat(chatID int64, args []string) (string, bool) {
backend, ok := gateway.E2EE.Backend()
if !ok {
return "OMEMO is not enabled on this transport", false
}
owner := e2ee.OwnedPeer(c.Session.Login, gateway.CHATJID(chatID, false))
if len(args) == 0 {
return c.omemoChatStatus(backend, owner)
}
switch args[0] {
case "on", "off":
if err := backend.SetOverride(owner, args[0] == "on"); err != nil {
return err.Error(), false
}
return fmt.Sprintf("OMEMO forced %s for this chat", args[0]), true
case "clear":
if err := backend.ClearOverride(owner); err != nil {
return err.Error(), false
}
return "OMEMO override cleared for this chat, now following the account-wide mode", true
case "devices":
return c.cmdOmemoDevices(backend, owner, args[1:])
default:
return "Usage: /omemo [on|off|clear|devices list|devices block <id>|devices accept <id>]", false
}
}
func (c *Client) omemoChatStatus(backend e2ee.Backend, owner e2ee.PeerID) (string, bool) {
mode, err := e2ee.ParseMode(c.Session.OMEMO)
if err != nil {
return err.Error(), false
}
override, isSet, err := backend.Override(owner)
if err != nil {
return err.Error(), false
}
active, err := e2ee.ShouldEncrypt(backend, owner, mode)
if err != nil {
return err.Error(), false
}
overrideText := "not set (following the account-wide mode)"
if isSet {
overrideText = fmt.Sprintf("forced %s", onOffString(override))
}
return fmt.Sprintf("Account-wide mode: %s\nThis chat's override: %s\nEffective right now: %s",
mode, overrideText, onOffString(active)), true
}
// cmdOmemoDevices lists, blocks, or accepts (unblocks) devices of owner's
// chat's real peer. TOFU trust itself always stays on (a never-before-seen
// device is always trusted automatically) - block/accept only manage a
// manual override layered on top of that, per Backend.BlockDevice's doc
// comment.
func (c *Client) cmdOmemoDevices(backend e2ee.Backend, owner e2ee.PeerID, args []string) (string, bool) {
peer := e2ee.PeerID(c.jid)
sub := "list"
if len(args) > 0 {
sub = args[0]
}
switch sub {
case "list":
devices, err := backend.Devices(owner, peer)
if err != nil {
return err.Error(), false
}
if len(devices) == 0 {
return "No known devices for this chat's peer yet", true
}
lines := make([]string, 0, len(devices))
for _, d := range devices {
lines = append(lines, fmt.Sprintf("%s: %s", d.ID, trustString(d.Trusted)))
}
return strings.Join(lines, "\n"), true
case "block", "accept":
if len(args) < 2 {
return notEnoughArguments, false
}
device := e2ee.DeviceID(args[1])
var err error
if sub == "block" {
err = backend.BlockDevice(owner, peer, device)
} else {
err = backend.UnblockDevice(owner, peer, device)
}
if err != nil {
return err.Error(), false
}
return fmt.Sprintf("Device %s %sed", device, sub), true
default:
return "Usage: /omemo devices [list|block <id>|accept <id>]", false
}
}
func onOffString(b bool) string {
if b {
return "on"
}
return "off"
}
func trustString(trusted bool) string {
if trusted {
return "trusted"
}
return "blocked"
}