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
105 lines
2.4 KiB
Go
105 lines
2.4 KiB
Go
package downloader
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"sort"
|
|
"sync"
|
|
|
|
"github.com/go-faster/errors"
|
|
|
|
"go.mau.fi/mautrix-telegram/pkg/gotd/crypto"
|
|
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
|
"go.mau.fi/mautrix-telegram/pkg/gotd/tgerr"
|
|
)
|
|
|
|
// ErrHashMismatch means that download hash verification was failed.
|
|
var ErrHashMismatch = errors.New("file hash mismatch")
|
|
|
|
type verifier struct {
|
|
client schema
|
|
|
|
hashes []tg.FileHash
|
|
offset int64
|
|
mux sync.Mutex
|
|
}
|
|
|
|
func newVerifier(client schema, hashes ...tg.FileHash) *verifier {
|
|
r := make([]tg.FileHash, len(hashes))
|
|
|
|
copy(r, hashes)
|
|
sort.SliceStable(r, func(i, j int) bool {
|
|
return r[i].Offset < r[j].Offset
|
|
})
|
|
|
|
return &verifier{client: client, hashes: r}
|
|
}
|
|
|
|
func (v *verifier) pop() (tg.FileHash, bool) {
|
|
if len(v.hashes) < 1 {
|
|
return tg.FileHash{}, false
|
|
}
|
|
|
|
// Pop and move.
|
|
hash := v.hashes[0]
|
|
copy(v.hashes, v.hashes[1:])
|
|
v.hashes[len(v.hashes)-1] = tg.FileHash{}
|
|
v.hashes = v.hashes[:len(v.hashes)-1]
|
|
|
|
return hash, true
|
|
}
|
|
|
|
func (v *verifier) update(hashes ...tg.FileHash) (tg.FileHash, bool) {
|
|
// If result is empty and queue is empty, so we can't return next hash.
|
|
if len(hashes) < 1 {
|
|
return tg.FileHash{}, false
|
|
}
|
|
|
|
// Sort hashes by offset.
|
|
// Usually Telegram server returns sorted parts, but...
|
|
// you never known what can they do.
|
|
sort.SliceStable(hashes, func(i, j int) bool {
|
|
return hashes[i].Offset < hashes[j].Offset
|
|
})
|
|
|
|
last := hashes[len(hashes)-1]
|
|
// Check if we have reached the end.
|
|
// If current state offset is equal the last offset + limit (right border)
|
|
// then we got all hashes.
|
|
if last.Offset == v.offset-int64(last.Limit) {
|
|
return tg.FileHash{}, false
|
|
}
|
|
|
|
// Otherwise, we update current offset and add hashes to the end of queue.
|
|
v.offset = last.Offset + int64(last.Limit)
|
|
v.hashes = append(v.hashes, hashes...)
|
|
return v.pop()
|
|
}
|
|
|
|
func (v *verifier) next(ctx context.Context) (tg.FileHash, bool, error) {
|
|
v.mux.Lock()
|
|
defer v.mux.Unlock()
|
|
|
|
hash, ok := v.pop()
|
|
if ok {
|
|
return hash, ok, nil
|
|
}
|
|
|
|
for {
|
|
hashes, err := v.client.Hashes(ctx, v.offset)
|
|
if flood, err := tgerr.FloodWait(ctx, err); err != nil {
|
|
if flood || tgerr.Is(err, tg.ErrTimeout) {
|
|
continue
|
|
}
|
|
return tg.FileHash{}, false, errors.Wrap(err, "get hashes")
|
|
}
|
|
|
|
hash, ok = v.update(hashes...)
|
|
return hash, ok, nil
|
|
}
|
|
}
|
|
|
|
func (v *verifier) verify(hash tg.FileHash, data []byte) bool {
|
|
return bytes.Equal(crypto.SHA256(data), hash.Hash)
|
|
}
|