chore: refresh gramsrv public release
This commit is contained in:
parent
75cebe8dbf
commit
70b6820474
1274 changed files with 378751 additions and 59919 deletions
260
internal/compat/layerwire/inbound.go
Normal file
260
internal/compat/layerwire/inbound.go
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
package layerwire
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/gotd/td/bin"
|
||||
)
|
||||
|
||||
// Canonical ids used to synthesize converted/defaulted values.
|
||||
const (
|
||||
inputUserID = 0xf21158c6 // inputUser user_id:long access_hash:long
|
||||
inputMessageID = 0xa676a322 // inputMessageID id:int
|
||||
boolFalseID = 0xbc799737 // boolFalse
|
||||
)
|
||||
|
||||
//go:embed schema/client-drift.tl
|
||||
var clientDriftSchema string
|
||||
|
||||
// driftModel holds the declared old-layout of each client-drift constructor.
|
||||
var driftModel = mustLoadDrift()
|
||||
|
||||
func mustLoadDrift() *schemaModel {
|
||||
m, err := parseSchemaModel(clientDriftSchema)
|
||||
if err != nil {
|
||||
panic("layerwire: parse client-drift schema: " + err.Error())
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// driftFieldRenames maps a canonical field that was renamed from the client's
|
||||
// old constructor: key "<qualified method>\x00<canonical field>" -> old field.
|
||||
// Pure schema diff cannot recover a rename, so it is declared here (data, not a
|
||||
// transform). It is the only thing a structural rename needs.
|
||||
var driftFieldRenames = map[string]string{
|
||||
"bots.exportBotToken\x00bot": "bot_id",
|
||||
}
|
||||
|
||||
// fieldConverter rewrites one field whose wire type changed between the old and
|
||||
// canonical layout. Keyed by "<oldTypeSig>-><newTypeSig>"; raw is the old field's
|
||||
// encoded bytes. Reusable across any method with the same type change.
|
||||
type fieldConverter func(raw []byte, out *bin.Buffer) error
|
||||
|
||||
var fieldConverters = map[string]fieldConverter{
|
||||
// id:Vector<int> -> id:Vector<InputMessage> (wrap each int in inputMessageID).
|
||||
"Vector<int>->Vector<InputMessage>": func(raw []byte, out *bin.Buffer) error {
|
||||
in := &bin.Buffer{Buf: raw}
|
||||
n, err := in.VectorHeader()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.PutVectorHeader(n)
|
||||
for i := 0; i < n; i++ {
|
||||
v, err := in.Int()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.PutID(inputMessageID)
|
||||
out.PutInt(v)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
// bot_id:long -> bot:InputUser{user_id, access_hash=0}.
|
||||
"long->InputUser": func(raw []byte, out *bin.Buffer) error {
|
||||
in := &bin.Buffer{Buf: raw}
|
||||
id, err := in.Long()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.PutID(inputUserID)
|
||||
out.PutLong(id)
|
||||
out.PutLong(0)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
// UpgradeInbound converts an old client's inbound request to canonical (227)
|
||||
// form so the normal gotd dispatcher can handle it. It unifies three data-driven
|
||||
// sources, all of which require no per-method handler code:
|
||||
// - inboundMethodUpgrades (generated from api.tl diff): official layer drift.
|
||||
// - clientMethodAliases (client_aliases.go): body-identical client drift.
|
||||
// - driftModel (client-drift.tl): body-different client drift, upgraded by the
|
||||
// generic engine below.
|
||||
//
|
||||
// ok=false means no upgrade applies. On ok=true the returned buffer (canonical
|
||||
// id + body) is what to dispatch.
|
||||
func UpgradeInbound(id uint32, in *bin.Buffer) (*bin.Buffer, bool, error) {
|
||||
if newID, ok := UpgradeMethodCRC(id); ok {
|
||||
if len(in.Buf) < 4 {
|
||||
return nil, false, fmt.Errorf("layerwire: short inbound buffer for %#08x", id)
|
||||
}
|
||||
// Copy rather than rewrite in place: never mutate the caller's buffer
|
||||
// (matches the body-transform path, which also returns a fresh buffer).
|
||||
out := &bin.Buffer{Buf: append([]byte(nil), in.Buf...)}
|
||||
binary.LittleEndian.PutUint32(out.Buf[:4], newID)
|
||||
return out, true, nil
|
||||
}
|
||||
if old := driftModel.byCRC[id]; old != nil {
|
||||
out, err := upgradeFromDrift(old, in)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("layerwire: upgrade %s (%#08x): %w", old.name, id, err)
|
||||
}
|
||||
return out, true, nil
|
||||
}
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
// IsClientDrift reports whether id is a client-private constructor (DrKLO
|
||||
// constructor drift), as opposed to official layer drift from api.tl.
|
||||
func IsClientDrift(id uint32) bool {
|
||||
if _, ok := clientMethodAliases[id]; ok {
|
||||
return true
|
||||
}
|
||||
return driftModel.byCRC[id] != nil
|
||||
}
|
||||
|
||||
// upgradeFromDrift rebuilds a canonical (227) request from an old client-drift
|
||||
// body, comparing the declared old layout to the canonical layout field by field.
|
||||
func upgradeFromDrift(old *ctorLayout, in *bin.Buffer) (*bin.Buffer, error) {
|
||||
target := canonical.byName[old.name]
|
||||
if target == nil {
|
||||
return nil, fmt.Errorf("no canonical method %q", old.name)
|
||||
}
|
||||
if err := in.ConsumeID(old.crc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Decode the old body: capture each present field's raw bytes + flag ints.
|
||||
vals := make(map[string][]byte, len(old.fields))
|
||||
present := make(map[string]bool, len(old.fields))
|
||||
oldFlags := make(map[string]uint32, 2)
|
||||
oldByName := make(map[string]*fieldLayout, len(old.fields))
|
||||
for i := range old.fields {
|
||||
f := &old.fields[i]
|
||||
oldByName[f.name] = f
|
||||
if f.isFlags {
|
||||
v, err := in.Uint32()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
oldFlags[f.name] = v
|
||||
continue
|
||||
}
|
||||
if f.conditional() && oldFlags[f.flagName]&(1<<uint(f.flagBit)) == 0 {
|
||||
continue
|
||||
}
|
||||
present[f.name] = true
|
||||
if f.kind == kindTrue {
|
||||
continue
|
||||
}
|
||||
pre := in.Buf
|
||||
if err := canonical.skipValue(in, f); err != nil {
|
||||
return nil, fmt.Errorf("decode old field %q: %w", f.name, err)
|
||||
}
|
||||
vals[f.name] = pre[:len(pre)-len(in.Buf)]
|
||||
}
|
||||
if in.Len() != 0 {
|
||||
return nil, fmt.Errorf("%d trailing bytes after old body", in.Len())
|
||||
}
|
||||
|
||||
// Emit the canonical body.
|
||||
out := &bin.Buffer{}
|
||||
out.PutID(target.crc)
|
||||
for i := range target.fields {
|
||||
nf := &target.fields[i]
|
||||
if nf.isFlags {
|
||||
out.PutUint32(oldFlags[nf.name]) // 0 when absent in old (new flags int)
|
||||
continue
|
||||
}
|
||||
oldName := nf.name
|
||||
if mapped, ok := driftFieldRenames[old.name+"\x00"+nf.name]; ok {
|
||||
oldName = mapped
|
||||
}
|
||||
if present[oldName] {
|
||||
of := oldByName[oldName]
|
||||
if of != nil && typeSig(of) != typeSig(nf) {
|
||||
conv := fieldConverters[typeSig(of)+"->"+typeSig(nf)]
|
||||
if conv == nil {
|
||||
return nil, fmt.Errorf("field %q: no converter %s->%s", nf.name, typeSig(of), typeSig(nf))
|
||||
}
|
||||
if err := conv(vals[oldName], out); err != nil {
|
||||
return nil, fmt.Errorf("field %q convert: %w", nf.name, err)
|
||||
}
|
||||
} else {
|
||||
out.Put(vals[oldName]) // shared field, identical wire (kindTrue => no bytes)
|
||||
}
|
||||
continue
|
||||
}
|
||||
// Canonical-only field absent in old.
|
||||
if nf.conditional() || nf.kind == kindTrue {
|
||||
continue // optional: leave absent (its flag bit is clear)
|
||||
}
|
||||
if err := writeDefault(nf, out); err != nil {
|
||||
return nil, fmt.Errorf("field %q default: %w", nf.name, err)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// writeDefault writes the zero value of a required canonical-only field.
|
||||
func writeDefault(f *fieldLayout, out *bin.Buffer) error {
|
||||
switch f.kind {
|
||||
case kindInt:
|
||||
out.PutInt(0)
|
||||
case kindLong:
|
||||
out.PutLong(0)
|
||||
case kindDouble:
|
||||
out.PutDouble(0)
|
||||
case kindInt128:
|
||||
out.PutInt128(bin.Int128{})
|
||||
case kindInt256:
|
||||
out.PutInt256(bin.Int256{})
|
||||
case kindBytes:
|
||||
out.PutBytes(nil)
|
||||
case kindString:
|
||||
out.PutString("")
|
||||
case kindBool:
|
||||
out.PutID(boolFalseID)
|
||||
case kindVector:
|
||||
out.PutVectorHeader(0)
|
||||
case kindVectorBare:
|
||||
out.PutInt(0)
|
||||
default:
|
||||
return fmt.Errorf("cannot default kind %d (boxed object needs a transform)", f.kind)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// typeSig is a stable wire-type signature for matching/converter lookup.
|
||||
func typeSig(f *fieldLayout) string {
|
||||
switch f.kind {
|
||||
case kindInt:
|
||||
return "int"
|
||||
case kindLong:
|
||||
return "long"
|
||||
case kindDouble:
|
||||
return "double"
|
||||
case kindInt128:
|
||||
return "int128"
|
||||
case kindInt256:
|
||||
return "int256"
|
||||
case kindBytes:
|
||||
return "bytes"
|
||||
case kindString:
|
||||
return "string"
|
||||
case kindBool:
|
||||
return "Bool"
|
||||
case kindTrue:
|
||||
return "true"
|
||||
case kindVector:
|
||||
return "Vector<" + typeSig(f.elem) + ">"
|
||||
case kindVectorBare:
|
||||
return "vector<" + typeSig(f.elem) + ">"
|
||||
case kindObject, kindBareObject:
|
||||
return f.typeName
|
||||
default:
|
||||
return fmt.Sprintf("kind%d", f.kind)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue