7a04f298d2
- update to latest telegram layer - remove some references to fields in tg.Entities that don't exist in the schema - originally added here: https://github.com/beeper/td/commit/820929062a2ba0104397bc01235ab58a9cff780e - referenced here - https://github.com/mautrix/telegramgo/commit/124f0967ed195b5a380c9bd02e170ada9710dde3 - https://github.com/mautrix/telegramgo/commit/4205047aab2e0639217148b5d125bfaab668bd8e
159 lines
3.9 KiB
Go
159 lines
3.9 KiB
Go
// Package session implements session storage.
|
|
package session
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
|
|
"github.com/go-faster/errors"
|
|
|
|
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
|
)
|
|
|
|
// Config is subset of tg.Config.
|
|
type Config struct {
|
|
// Indicates that telegram is probably censored by governments/ISPs in the current region
|
|
BlockedMode bool
|
|
// Whether to forcefully try connecting using IPv6 dcOptions¹
|
|
//
|
|
// Links:
|
|
// 1) https://core.telegram.org/type/DcOption
|
|
ForceTryIpv6 bool
|
|
// Current date at the server
|
|
Date int
|
|
// Expiration date of this config: when it expires it'll have to be refetched using help
|
|
// getConfig¹
|
|
//
|
|
// Links:
|
|
// 1) https://core.telegram.org/method/help.getConfig
|
|
Expires int
|
|
// Whether we're connected to the test DCs
|
|
TestMode bool
|
|
// ID of the DC that returned the reply
|
|
ThisDC int
|
|
// DC IP list
|
|
DCOptions []tg.DCOption
|
|
// Domain name for fetching encrypted DC list from DNS TXT record
|
|
DCTxtDomainName string
|
|
// Temporary passport¹ sessions
|
|
//
|
|
// Links:
|
|
// 1) https://core.telegram.org/passport
|
|
//
|
|
// Use SetTmpSessions and GetTmpSessions helpers.
|
|
TmpSessions int
|
|
// DC ID to use to download webfiles¹
|
|
//
|
|
// Links:
|
|
// 1) https://core.telegram.org/api/files#downloading-webfiles
|
|
WebfileDCID int
|
|
}
|
|
|
|
// ConfigFromTG converts tg.Config to Config.
|
|
//
|
|
// Note that Config is the subset of tg.Config, so data loss is possible.
|
|
func ConfigFromTG(c tg.Config) Config {
|
|
return Config{
|
|
BlockedMode: c.BlockedMode,
|
|
ForceTryIpv6: c.ForceTryIpv6,
|
|
Date: c.Date,
|
|
Expires: c.Expires,
|
|
TestMode: c.TestMode,
|
|
ThisDC: c.ThisDC,
|
|
DCOptions: c.DCOptions,
|
|
DCTxtDomainName: c.DCTxtDomainName,
|
|
WebfileDCID: c.WebfileDCID,
|
|
TmpSessions: c.TmpSessions,
|
|
}
|
|
}
|
|
|
|
// TG returns tg.Config from Config.
|
|
//
|
|
// Note that config is the subset of tg.Config, so some fields will be unset.
|
|
func (c Config) TG() tg.Config {
|
|
return tg.Config{
|
|
BlockedMode: c.BlockedMode,
|
|
ForceTryIpv6: c.ForceTryIpv6,
|
|
Date: c.Date,
|
|
Expires: c.Expires,
|
|
TestMode: c.TestMode,
|
|
ThisDC: c.ThisDC,
|
|
DCOptions: c.DCOptions,
|
|
DCTxtDomainName: c.DCTxtDomainName,
|
|
WebfileDCID: c.WebfileDCID,
|
|
TmpSessions: c.TmpSessions,
|
|
}
|
|
}
|
|
|
|
// Data of session.
|
|
type Data struct {
|
|
Config Config
|
|
DC int
|
|
Addr string
|
|
AuthKey []byte
|
|
AuthKeyID []byte
|
|
Salt int64
|
|
}
|
|
|
|
// Storage is secure persistent storage for client session.
|
|
//
|
|
// NB: Implementation security is important, attacker can abuse it not only for
|
|
// connecting as authenticated user or bot, but even decrypting previous
|
|
// messages in some situations.
|
|
type Storage interface {
|
|
LoadSession(ctx context.Context) ([]byte, error)
|
|
StoreSession(ctx context.Context, data []byte) error
|
|
}
|
|
|
|
// ErrNotFound means that session is not found in storage.
|
|
var ErrNotFound = errors.New("session storage: not found")
|
|
|
|
// Loader wraps Storage implementing Data (un-)marshaling.
|
|
type Loader struct {
|
|
Storage Storage
|
|
}
|
|
|
|
type jsonData struct {
|
|
Version int
|
|
Data Data
|
|
}
|
|
|
|
const latestVersion = 1
|
|
|
|
// Load loads Data from Storage.
|
|
func (l *Loader) Load(ctx context.Context) (*Data, error) {
|
|
buf, err := l.Storage.LoadSession(ctx)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "load")
|
|
}
|
|
if len(buf) == 0 {
|
|
return nil, ErrNotFound
|
|
}
|
|
|
|
var v jsonData
|
|
if err := json.Unmarshal(buf, &v); err != nil {
|
|
return nil, errors.Wrap(err, "unmarshal")
|
|
}
|
|
if v.Version != latestVersion {
|
|
// HACK(ernado): backward compatibility super shenanigan.
|
|
return nil, errors.Wrapf(ErrNotFound, "version mismatch (%d != %d)", v.Version, latestVersion)
|
|
}
|
|
return &v.Data, err
|
|
}
|
|
|
|
// Save saves Data to Storage.
|
|
func (l *Loader) Save(ctx context.Context, data *Data) error {
|
|
v := jsonData{
|
|
Version: latestVersion,
|
|
Data: *data,
|
|
}
|
|
buf, err := json.Marshal(v)
|
|
if err != nil {
|
|
return errors.Wrap(err, "marshal")
|
|
}
|
|
if err := l.Storage.StoreSession(ctx, buf); err != nil {
|
|
return errors.Wrap(err, "store")
|
|
}
|
|
return nil
|
|
}
|