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 |devices accept ]", 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 |accept ]", false } } func onOffString(b bool) string { if b { return "on" } return "off" } func trustString(trusted bool) string { if trusted { return "trusted" } return "blocked" }