move gotd fork into repo. (#111)
- 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
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user