266 lines
10 KiB
Go
266 lines
10 KiB
Go
// Copyright (c) 2025 Tulir Asokan
|
|
//
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
package id
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"slices"
|
|
)
|
|
|
|
type RoomVersion string
|
|
|
|
const (
|
|
RoomV0 RoomVersion = "" // No room version, used for rooms created before room versions were introduced, equivalent to v1
|
|
RoomV1 RoomVersion = "1"
|
|
RoomV2 RoomVersion = "2"
|
|
RoomV3 RoomVersion = "3"
|
|
RoomV4 RoomVersion = "4"
|
|
RoomV5 RoomVersion = "5"
|
|
RoomV6 RoomVersion = "6"
|
|
RoomV7 RoomVersion = "7"
|
|
RoomV8 RoomVersion = "8"
|
|
RoomV9 RoomVersion = "9"
|
|
RoomV10 RoomVersion = "10"
|
|
RoomV11 RoomVersion = "11"
|
|
RoomV12 RoomVersion = "12"
|
|
)
|
|
|
|
func (rv RoomVersion) Equals(versions ...RoomVersion) bool {
|
|
return slices.Contains(versions, rv)
|
|
}
|
|
|
|
func (rv RoomVersion) NotEquals(versions ...RoomVersion) bool {
|
|
return !rv.Equals(versions...)
|
|
}
|
|
|
|
var ErrUnknownRoomVersion = errors.New("unknown room version")
|
|
|
|
func (rv RoomVersion) unknownVersionError() error {
|
|
return fmt.Errorf("%w %s", ErrUnknownRoomVersion, rv)
|
|
}
|
|
|
|
func (rv RoomVersion) IsKnown() bool {
|
|
switch rv {
|
|
case RoomV0, RoomV1, RoomV2, RoomV3, RoomV4, RoomV5, RoomV6, RoomV7, RoomV8, RoomV9, RoomV10, RoomV11, RoomV12:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
type StateResVersion int
|
|
|
|
const (
|
|
// StateResV1 is the original state resolution algorithm.
|
|
StateResV1 StateResVersion = 0
|
|
// StateResV2 is state resolution v2 introduced by https://github.com/matrix-org/matrix-spec-proposals/pull/1759
|
|
StateResV2 StateResVersion = 1
|
|
// StateResV2_1 is state resolution v2.1 introduced by https://github.com/matrix-org/matrix-spec-proposals/pull/4297
|
|
StateResV2_1 StateResVersion = 2
|
|
)
|
|
|
|
// StateResVersion returns the version of the state resolution algorithm used by this room version.
|
|
func (rv RoomVersion) StateResVersion() StateResVersion {
|
|
switch rv {
|
|
case RoomV0, RoomV1:
|
|
return StateResV1
|
|
case RoomV2, RoomV3, RoomV4, RoomV5, RoomV6, RoomV7, RoomV8, RoomV9, RoomV10, RoomV11:
|
|
return StateResV2
|
|
case RoomV12:
|
|
return StateResV2_1
|
|
default:
|
|
panic(rv.unknownVersionError())
|
|
}
|
|
}
|
|
|
|
type EventIDFormat int
|
|
|
|
const (
|
|
// EventIDFormatCustom is the original format used by room v1 and v2.
|
|
// Event IDs in this format are an arbitrary string followed by a colon and the server name.
|
|
EventIDFormatCustom EventIDFormat = 0
|
|
// EventIDFormatBase64 is the format used by room v3 introduced by https://github.com/matrix-org/matrix-spec-proposals/pull/1659.
|
|
// Event IDs in this format are the standard unpadded base64-encoded SHA256 reference hash of the event.
|
|
EventIDFormatBase64 EventIDFormat = 1
|
|
// EventIDFormatURLSafeBase64 is the format used by room v4 and later introduced by https://github.com/matrix-org/matrix-spec-proposals/pull/2002.
|
|
// Event IDs in this format are the url-safe unpadded base64-encoded SHA256 reference hash of the event.
|
|
EventIDFormatURLSafeBase64 EventIDFormat = 2
|
|
)
|
|
|
|
// EventIDFormat returns the format of event IDs used by this room version.
|
|
func (rv RoomVersion) EventIDFormat() EventIDFormat {
|
|
switch rv {
|
|
case RoomV0, RoomV1, RoomV2:
|
|
return EventIDFormatCustom
|
|
case RoomV3:
|
|
return EventIDFormatBase64
|
|
default:
|
|
return EventIDFormatURLSafeBase64
|
|
}
|
|
}
|
|
|
|
/////////////////////
|
|
// Room v5 changes //
|
|
/////////////////////
|
|
// https://github.com/matrix-org/matrix-spec-proposals/pull/2077
|
|
|
|
// EnforceSigningKeyValidity returns true if the `valid_until_ts` field of federation signing keys
|
|
// must be enforced on received events.
|
|
//
|
|
// See https://github.com/matrix-org/matrix-spec-proposals/pull/2076
|
|
func (rv RoomVersion) EnforceSigningKeyValidity() bool {
|
|
return rv.NotEquals(RoomV0, RoomV1, RoomV2, RoomV3, RoomV4)
|
|
}
|
|
|
|
/////////////////////
|
|
// Room v6 changes //
|
|
/////////////////////
|
|
// https://github.com/matrix-org/matrix-spec-proposals/pull/2240
|
|
|
|
// SpecialCasedAliasesAuth returns true if the `m.room.aliases` event authorization is special cased
|
|
// to only always allow servers to modify the state event with their own server name as state key.
|
|
// This also implies that the `aliases` field is protected from redactions.
|
|
//
|
|
// See https://github.com/matrix-org/matrix-spec-proposals/pull/2432
|
|
func (rv RoomVersion) SpecialCasedAliasesAuth() bool {
|
|
return rv.Equals(RoomV0, RoomV1, RoomV2, RoomV3, RoomV4, RoomV5)
|
|
}
|
|
|
|
// ForbidFloatsAndBigInts returns true if floats and integers greater than 2^53-1 or lower than -2^53+1 are forbidden everywhere.
|
|
//
|
|
// See https://github.com/matrix-org/matrix-spec-proposals/pull/2540
|
|
func (rv RoomVersion) ForbidFloatsAndBigInts() bool {
|
|
return rv.NotEquals(RoomV0, RoomV1, RoomV2, RoomV3, RoomV4, RoomV5)
|
|
}
|
|
|
|
// NotificationsPowerLevels returns true if the `notifications` field in `m.room.power_levels` is validated in event auth.
|
|
// However, the field is not protected from redactions.
|
|
//
|
|
// See https://github.com/matrix-org/matrix-spec-proposals/pull/2209
|
|
func (rv RoomVersion) NotificationsPowerLevels() bool {
|
|
return rv.NotEquals(RoomV0, RoomV1, RoomV2, RoomV3, RoomV4, RoomV5)
|
|
}
|
|
|
|
/////////////////////
|
|
// Room v7 changes //
|
|
/////////////////////
|
|
// https://github.com/matrix-org/matrix-spec-proposals/pull/2998
|
|
|
|
// Knocks returns true if the `knock` join rule is supported.
|
|
//
|
|
// See https://github.com/matrix-org/matrix-spec-proposals/pull/2403
|
|
func (rv RoomVersion) Knocks() bool {
|
|
return rv.NotEquals(RoomV0, RoomV1, RoomV2, RoomV3, RoomV4, RoomV5, RoomV6)
|
|
}
|
|
|
|
/////////////////////
|
|
// Room v8 changes //
|
|
/////////////////////
|
|
// https://github.com/matrix-org/matrix-spec-proposals/pull/3289
|
|
|
|
// RestrictedJoins returns true if the `restricted` join rule is supported.
|
|
// This also implies that the `allow` field in the `m.room.join_rules` event is supported and protected from redactions.
|
|
//
|
|
// See https://github.com/matrix-org/matrix-spec-proposals/pull/3083
|
|
func (rv RoomVersion) RestrictedJoins() bool {
|
|
return rv.NotEquals(RoomV0, RoomV1, RoomV2, RoomV3, RoomV4, RoomV5, RoomV6, RoomV7)
|
|
}
|
|
|
|
/////////////////////
|
|
// Room v9 changes //
|
|
/////////////////////
|
|
// https://github.com/matrix-org/matrix-spec-proposals/pull/3375
|
|
|
|
// RestrictedJoinsFix returns true if the `join_authorised_via_users_server` field in `m.room.member` events is protected from redactions.
|
|
//
|
|
// See https://github.com/matrix-org/matrix-spec-proposals/pull/3375
|
|
func (rv RoomVersion) RestrictedJoinsFix() bool {
|
|
return rv.RestrictedJoins() && rv != RoomV8
|
|
}
|
|
|
|
//////////////////////
|
|
// Room v10 changes //
|
|
//////////////////////
|
|
// https://github.com/matrix-org/matrix-spec-proposals/pull/3604
|
|
|
|
// ValidatePowerLevelInts returns true if the known values in `m.room.power_levels` must be integers (and not strings).
|
|
//
|
|
// See https://github.com/matrix-org/matrix-spec-proposals/pull/3667
|
|
func (rv RoomVersion) ValidatePowerLevelInts() bool {
|
|
return rv.NotEquals(RoomV0, RoomV1, RoomV2, RoomV3, RoomV4, RoomV5, RoomV6, RoomV7, RoomV8, RoomV9)
|
|
}
|
|
|
|
// KnockRestricted returns true if the `knock_restricted` join rule is supported.
|
|
//
|
|
// See https://github.com/matrix-org/matrix-spec-proposals/pull/3787
|
|
func (rv RoomVersion) KnockRestricted() bool {
|
|
return rv.NotEquals(RoomV0, RoomV1, RoomV2, RoomV3, RoomV4, RoomV5, RoomV6, RoomV7, RoomV8, RoomV9)
|
|
}
|
|
|
|
//////////////////////
|
|
// Room v11 changes //
|
|
//////////////////////
|
|
// https://github.com/matrix-org/matrix-spec-proposals/pull/3820
|
|
|
|
// CreatorInContent returns true if the `m.room.create` event has a `creator` field in content.
|
|
//
|
|
// See https://github.com/matrix-org/matrix-spec-proposals/pull/2175
|
|
func (rv RoomVersion) CreatorInContent() bool {
|
|
return rv.Equals(RoomV0, RoomV1, RoomV2, RoomV3, RoomV4, RoomV5, RoomV6, RoomV7, RoomV8, RoomV9, RoomV10)
|
|
}
|
|
|
|
// RedactsInContent returns true if the `m.room.redaction` event has the `redacts` field in content instead of at the top level.
|
|
// The redaction protection is also moved from the top level to the content field.
|
|
//
|
|
// See https://github.com/matrix-org/matrix-spec-proposals/pull/2174
|
|
// (and https://github.com/matrix-org/matrix-spec-proposals/pull/2176 for the redaction protection).
|
|
func (rv RoomVersion) RedactsInContent() bool {
|
|
return rv.NotEquals(RoomV0, RoomV1, RoomV2, RoomV3, RoomV4, RoomV5, RoomV6, RoomV7, RoomV8, RoomV9, RoomV10)
|
|
}
|
|
|
|
// UpdatedRedactionRules returns true if various updates to the redaction algorithm are applied.
|
|
//
|
|
// Specifically:
|
|
//
|
|
// * the `membership`, `origin`, and `prev_state` fields at the top level of all events are no longer protected.
|
|
// * the entire content of `m.room.create` is protected.
|
|
// * the `redacts` field in `m.room.redaction` content is protected instead of the top-level field.
|
|
// * the `m.room.power_levels` event protects the `invite` field in content.
|
|
// * the `signed` field inside the `third_party_invite` field in content of `m.room.member` events is protected.
|
|
//
|
|
// See https://github.com/matrix-org/matrix-spec-proposals/pull/2176,
|
|
// https://github.com/matrix-org/matrix-spec-proposals/pull/3821, and
|
|
// https://github.com/matrix-org/matrix-spec-proposals/pull/3989
|
|
func (rv RoomVersion) UpdatedRedactionRules() bool {
|
|
return rv.NotEquals(RoomV0, RoomV1, RoomV2, RoomV3, RoomV4, RoomV5, RoomV6, RoomV7, RoomV8, RoomV9, RoomV10)
|
|
}
|
|
|
|
//////////////////////
|
|
// Room v12 changes //
|
|
//////////////////////
|
|
// https://github.com/matrix-org/matrix-spec-proposals/pull/4304
|
|
|
|
// Return value of StateResVersion was changed to StateResV2_1
|
|
|
|
// PrivilegedRoomCreators returns true if the creator(s) of a room always have infinite power level.
|
|
// This also implies that the `m.room.create` event has an `additional_creators` field,
|
|
// and that the creators can't be present in the `m.room.power_levels` event.
|
|
//
|
|
// See https://github.com/matrix-org/matrix-spec-proposals/pull/4289
|
|
func (rv RoomVersion) PrivilegedRoomCreators() bool {
|
|
return rv.NotEquals(RoomV0, RoomV1, RoomV2, RoomV3, RoomV4, RoomV5, RoomV6, RoomV7, RoomV8, RoomV9, RoomV10, RoomV11)
|
|
}
|
|
|
|
// RoomIDIsCreateEventID returns true if the ID of rooms is the same as the ID of the `m.room.create` event.
|
|
// This also implies that `m.room.create` events do not have a `room_id` field.
|
|
//
|
|
// See https://github.com/matrix-org/matrix-spec-proposals/pull/4291
|
|
func (rv RoomVersion) RoomIDIsCreateEventID() bool {
|
|
return rv.NotEquals(RoomV0, RoomV1, RoomV2, RoomV3, RoomV4, RoomV5, RoomV6, RoomV7, RoomV8, RoomV9, RoomV10, RoomV11)
|
|
}
|