Update dependencies and build to go1.22 (#2113)
* Update dependencies and build to go1.22 * Fix api changes wrt to dependencies * Update golangci config
This commit is contained in:
90
vendor/go.mau.fi/whatsmeow/download.go
vendored
90
vendor/go.mau.fi/whatsmeow/download.go
vendored
@@ -10,6 +10,7 @@ import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
@@ -17,9 +18,11 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.mau.fi/util/retryafter"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
|
||||
"go.mau.fi/whatsmeow/binary/armadillo/waMediaTransport"
|
||||
waProto "go.mau.fi/whatsmeow/binary/proto"
|
||||
"go.mau.fi/whatsmeow/socket"
|
||||
"go.mau.fi/whatsmeow/util/cbcutil"
|
||||
@@ -208,6 +211,10 @@ func (cli *Client) Download(msg DownloadableMessage) ([]byte, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *Client) DownloadFB(transport *waMediaTransport.WAMediaTransport_Integral, mediaType MediaType) ([]byte, error) {
|
||||
return cli.DownloadMediaWithPath(transport.GetDirectPath(), transport.GetFileEncSHA256(), transport.GetFileSHA256(), transport.GetMediaKey(), -1, mediaType, mediaTypeToMMSType[mediaType])
|
||||
}
|
||||
|
||||
// DownloadMediaWithPath downloads an attachment by manually specifying the path and encryption details.
|
||||
func (cli *Client) DownloadMediaWithPath(directPath string, encFileHash, fileHash, mediaKey []byte, fileLength int, mediaType MediaType, mmsType string) (data []byte, err error) {
|
||||
var mediaConn *MediaConn
|
||||
@@ -219,15 +226,16 @@ func (cli *Client) DownloadMediaWithPath(directPath string, encFileHash, fileHas
|
||||
mmsType = mediaTypeToMMSType[mediaType]
|
||||
}
|
||||
for i, host := range mediaConn.Hosts {
|
||||
// TODO omit hash for unencrypted media?
|
||||
mediaURL := fmt.Sprintf("https://%s%s&hash=%s&mms-type=%s&__wa-mms=", host.Hostname, directPath, base64.URLEncoding.EncodeToString(encFileHash), mmsType)
|
||||
data, err = cli.downloadAndDecrypt(mediaURL, mediaKey, mediaType, fileLength, encFileHash, fileHash)
|
||||
// TODO there are probably some errors that shouldn't retry
|
||||
if err != nil {
|
||||
if i >= len(mediaConn.Hosts)-1 {
|
||||
return nil, fmt.Errorf("failed to download media from last host: %w", err)
|
||||
}
|
||||
cli.Log.Warnf("Failed to download media: %s, trying with next host...", err)
|
||||
if err == nil {
|
||||
return
|
||||
} else if i >= len(mediaConn.Hosts)-1 {
|
||||
return nil, fmt.Errorf("failed to download media from last host: %w", err)
|
||||
}
|
||||
// TODO there are probably some errors that shouldn't retry
|
||||
cli.Log.Warnf("Failed to download media: %s, trying with next host...", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -235,8 +243,11 @@ func (cli *Client) DownloadMediaWithPath(directPath string, encFileHash, fileHas
|
||||
func (cli *Client) downloadAndDecrypt(url string, mediaKey []byte, appInfo MediaType, fileLength int, fileEncSha256, fileSha256 []byte) (data []byte, err error) {
|
||||
iv, cipherKey, macKey, _ := getMediaKeys(mediaKey, appInfo)
|
||||
var ciphertext, mac []byte
|
||||
if ciphertext, mac, err = cli.downloadEncryptedMediaWithRetries(url, fileEncSha256); err != nil {
|
||||
if ciphertext, mac, err = cli.downloadPossiblyEncryptedMediaWithRetries(url, fileEncSha256); err != nil {
|
||||
|
||||
} else if mediaKey == nil && fileEncSha256 == nil && mac == nil {
|
||||
// Unencrypted media, just return the downloaded data
|
||||
data = ciphertext
|
||||
} else if err = validateMedia(iv, ciphertext, macKey, mac); err != nil {
|
||||
|
||||
} else if data, err = cbcutil.Decrypt(cipherKey, iv, ciphertext); err != nil {
|
||||
@@ -254,52 +265,59 @@ func getMediaKeys(mediaKey []byte, appInfo MediaType) (iv, cipherKey, macKey, re
|
||||
return mediaKeyExpanded[:16], mediaKeyExpanded[16:48], mediaKeyExpanded[48:80], mediaKeyExpanded[80:]
|
||||
}
|
||||
|
||||
func (cli *Client) downloadEncryptedMediaWithRetries(url string, checksum []byte) (file, mac []byte, err error) {
|
||||
func shouldRetryMediaDownload(err error) bool {
|
||||
var netErr net.Error
|
||||
var httpErr DownloadHTTPError
|
||||
return errors.As(err, &netErr) ||
|
||||
strings.HasPrefix(err.Error(), "stream error:") || // hacky check for http2 errors
|
||||
(errors.As(err, &httpErr) && retryafter.Should(httpErr.StatusCode, true))
|
||||
}
|
||||
|
||||
func (cli *Client) downloadPossiblyEncryptedMediaWithRetries(url string, checksum []byte) (file, mac []byte, err error) {
|
||||
for retryNum := 0; retryNum < 5; retryNum++ {
|
||||
file, mac, err = cli.downloadEncryptedMedia(url, checksum)
|
||||
if err == nil {
|
||||
if checksum == nil {
|
||||
file, err = cli.downloadMedia(url)
|
||||
} else {
|
||||
file, mac, err = cli.downloadEncryptedMedia(url, checksum)
|
||||
}
|
||||
if err == nil || !shouldRetryMediaDownload(err) {
|
||||
return
|
||||
}
|
||||
netErr, ok := err.(net.Error)
|
||||
if !ok {
|
||||
// Not a network error, don't retry
|
||||
return
|
||||
retryDuration := time.Duration(retryNum+1) * time.Second
|
||||
var httpErr DownloadHTTPError
|
||||
if errors.As(err, &httpErr) {
|
||||
retryDuration = retryafter.Parse(httpErr.Response.Header.Get("Retry-After"), retryDuration)
|
||||
}
|
||||
cli.Log.Warnf("Failed to download media due to network error: %w, retrying...", netErr)
|
||||
time.Sleep(time.Duration(retryNum+1) * time.Second)
|
||||
cli.Log.Warnf("Failed to download media due to network error: %w, retrying in %s...", err, retryDuration)
|
||||
time.Sleep(retryDuration)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cli *Client) downloadEncryptedMedia(url string, checksum []byte) (file, mac []byte, err error) {
|
||||
var req *http.Request
|
||||
req, err = http.NewRequest(http.MethodGet, url, nil)
|
||||
func (cli *Client) downloadMedia(url string) ([]byte, error) {
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to prepare request: %w", err)
|
||||
return
|
||||
return nil, fmt.Errorf("failed to prepare request: %w", err)
|
||||
}
|
||||
req.Header.Set("Origin", socket.Origin)
|
||||
req.Header.Set("Referer", socket.Origin+"/")
|
||||
var resp *http.Response
|
||||
resp, err = cli.http.Do(req)
|
||||
if cli.MessengerConfig != nil {
|
||||
req.Header.Set("User-Agent", cli.MessengerConfig.UserAgent)
|
||||
}
|
||||
// TODO user agent for whatsapp downloads?
|
||||
resp, err := cli.http.Do(req)
|
||||
if err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
if resp.StatusCode == http.StatusForbidden {
|
||||
err = ErrMediaDownloadFailedWith403
|
||||
} else if resp.StatusCode == http.StatusNotFound {
|
||||
err = ErrMediaDownloadFailedWith404
|
||||
} else if resp.StatusCode == http.StatusGone {
|
||||
err = ErrMediaDownloadFailedWith410
|
||||
} else {
|
||||
err = fmt.Errorf("download failed with status code %d", resp.StatusCode)
|
||||
}
|
||||
return
|
||||
return nil, DownloadHTTPError{Response: resp}
|
||||
}
|
||||
var data []byte
|
||||
data, err = io.ReadAll(resp.Body)
|
||||
return io.ReadAll(resp.Body)
|
||||
}
|
||||
|
||||
func (cli *Client) downloadEncryptedMedia(url string, checksum []byte) (file, mac []byte, err error) {
|
||||
data, err := cli.downloadMedia(url)
|
||||
if err != nil {
|
||||
return
|
||||
} else if len(data) <= 10 {
|
||||
|
||||
Reference in New Issue
Block a user