mirror of
https://dev.narayana.im/narayana/telegabber.git
synced 2026-08-05 04:07:07 +00:00
195 lines
6 KiB
Go
195 lines
6 KiB
Go
package telegram
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"dev.narayana.im/narayana/telegabber/e2ee"
|
|
"dev.narayana.im/narayana/telegabber/xmpp/gateway"
|
|
)
|
|
|
|
// OmemoDeviceStatus is one known device of a chat's peer - shared data
|
|
// backing both the text "/omemo devices" command and the ad-hoc command's
|
|
// form (xmpp/omemo_command.go), so both surfaces always agree.
|
|
type OmemoDeviceStatus struct {
|
|
ID string
|
|
Trusted bool
|
|
}
|
|
|
|
// OmemoChatStatus is a chat's current OMEMO state - shared data backing
|
|
// both the text "/omemo" command and the ad-hoc command's form.
|
|
type OmemoChatStatus struct {
|
|
Mode string // account-wide mode: "on"/"auto"/"off" (e2ee.Mode.String())
|
|
OverrideSet bool
|
|
Override bool // meaningful only if OverrideSet
|
|
Effective bool // e2ee.ShouldEncrypt's actual verdict right now
|
|
Devices []OmemoDeviceStatus
|
|
}
|
|
|
|
// GetOmemoChatStatus reports chatID's current OMEMO state.
|
|
func (c *Client) GetOmemoChatStatus(chatID int64) (OmemoChatStatus, error) {
|
|
backend, ok := gateway.E2EE.Backend()
|
|
if !ok {
|
|
return OmemoChatStatus{}, errors.New("OMEMO is not enabled on this transport")
|
|
}
|
|
owner := e2ee.OwnedPeer(c.Session.Login, gateway.CHATJID(chatID, false))
|
|
|
|
mode, err := e2ee.ParseMode(c.Session.OMEMO)
|
|
if err != nil {
|
|
return OmemoChatStatus{}, err
|
|
}
|
|
override, isSet, err := backend.Override(owner)
|
|
if err != nil {
|
|
return OmemoChatStatus{}, err
|
|
}
|
|
active, err := e2ee.ShouldEncrypt(backend, owner, mode)
|
|
if err != nil {
|
|
return OmemoChatStatus{}, err
|
|
}
|
|
devices, err := backend.Devices(owner, e2ee.PeerID(c.jid))
|
|
if err != nil {
|
|
return OmemoChatStatus{}, err
|
|
}
|
|
|
|
statuses := make([]OmemoDeviceStatus, len(devices))
|
|
for i, d := range devices {
|
|
statuses[i] = OmemoDeviceStatus{ID: string(d.ID), Trusted: d.Trusted}
|
|
}
|
|
|
|
return OmemoChatStatus{
|
|
Mode: mode.String(),
|
|
OverrideSet: isSet,
|
|
Override: override,
|
|
Effective: active,
|
|
Devices: statuses,
|
|
}, nil
|
|
}
|
|
|
|
// SetOmemoChatOverride forces (on non-nil) or clears (on nil) chatID's
|
|
// OMEMO override.
|
|
func (c *Client) SetOmemoChatOverride(chatID int64, on *bool) error {
|
|
backend, ok := gateway.E2EE.Backend()
|
|
if !ok {
|
|
return errors.New("OMEMO is not enabled on this transport")
|
|
}
|
|
owner := e2ee.OwnedPeer(c.Session.Login, gateway.CHATJID(chatID, false))
|
|
if on == nil {
|
|
return backend.ClearOverride(owner)
|
|
}
|
|
return backend.SetOverride(owner, *on)
|
|
}
|
|
|
|
// SetOmemoDeviceBlocked blocks or unblocks one of chatID's peer's devices.
|
|
func (c *Client) SetOmemoDeviceBlocked(chatID int64, deviceID string, blocked bool) error {
|
|
backend, ok := gateway.E2EE.Backend()
|
|
if !ok {
|
|
return errors.New("OMEMO is not enabled on this transport")
|
|
}
|
|
owner := e2ee.OwnedPeer(c.Session.Login, gateway.CHATJID(chatID, false))
|
|
peer := e2ee.PeerID(c.jid)
|
|
if blocked {
|
|
return backend.BlockDevice(owner, peer, e2ee.DeviceID(deviceID))
|
|
}
|
|
return backend.UnblockDevice(owner, peer, e2ee.DeviceID(deviceID))
|
|
}
|
|
|
|
// cmdOmemoChat implements the chat-scoped "/omemo" text 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; "clear" removes that
|
|
// override, reverting to following the account-wide mode; "devices ..."
|
|
// manages this chat's peer's device trust (see cmdOmemoDevices). The
|
|
// ad-hoc command surface (xmpp/omemo_command.go) offers the same controls
|
|
// as a form, built from the same GetOmemoChatStatus/SetOmemoChatOverride/
|
|
// SetOmemoDeviceBlocked methods this uses.
|
|
func (c *Client) cmdOmemoChat(chatID int64, args []string) (string, bool) {
|
|
if len(args) == 0 {
|
|
return c.omemoChatStatusString(chatID)
|
|
}
|
|
|
|
switch args[0] {
|
|
case "on", "off":
|
|
on := args[0] == "on"
|
|
if err := c.SetOmemoChatOverride(chatID, &on); err != nil {
|
|
return err.Error(), false
|
|
}
|
|
return fmt.Sprintf("OMEMO forced %s for this chat", args[0]), true
|
|
case "clear":
|
|
if err := c.SetOmemoChatOverride(chatID, nil); 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(chatID, args[1:])
|
|
default:
|
|
return "Usage: /omemo [on|off|clear|devices list|devices block <id>|devices accept <id>]", false
|
|
}
|
|
}
|
|
|
|
func (c *Client) omemoChatStatusString(chatID int64) (string, bool) {
|
|
status, err := c.GetOmemoChatStatus(chatID)
|
|
if err != nil {
|
|
return err.Error(), false
|
|
}
|
|
|
|
overrideText := "not set (following the account-wide mode)"
|
|
if status.OverrideSet {
|
|
overrideText = fmt.Sprintf("forced %s", onOffString(status.Override))
|
|
}
|
|
|
|
return fmt.Sprintf("Account-wide mode: %s\nThis chat's override: %s\nEffective right now: %s",
|
|
status.Mode, overrideText, onOffString(status.Effective)), true
|
|
}
|
|
|
|
// cmdOmemoDevices lists, blocks, or accepts (unblocks) devices of chatID'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 e2ee.Backend.BlockDevice's
|
|
// doc comment.
|
|
func (c *Client) cmdOmemoDevices(chatID int64, args []string) (string, bool) {
|
|
sub := "list"
|
|
if len(args) > 0 {
|
|
sub = args[0]
|
|
}
|
|
|
|
switch sub {
|
|
case "list":
|
|
status, err := c.GetOmemoChatStatus(chatID)
|
|
if err != nil {
|
|
return err.Error(), false
|
|
}
|
|
if len(status.Devices) == 0 {
|
|
return "No known devices for this chat's peer yet", true
|
|
}
|
|
lines := make([]string, 0, len(status.Devices))
|
|
for _, d := range status.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
|
|
}
|
|
if err := c.SetOmemoDeviceBlocked(chatID, args[1], sub == "block"); err != nil {
|
|
return err.Error(), false
|
|
}
|
|
return fmt.Sprintf("Device %s %sed", args[1], 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"
|
|
}
|