github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/chat/attachments/downloader.go (about)

     1  package attachments
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  
     9  	"github.com/keybase/client/go/chat/globals"
    10  	"github.com/keybase/client/go/libkb"
    11  	"github.com/keybase/client/go/protocol/chat1"
    12  	"github.com/keybase/client/go/protocol/gregor1"
    13  	"github.com/keybase/go-framed-msgpack-rpc/rpc"
    14  	"golang.org/x/net/context"
    15  )
    16  
    17  func SinkFromFilename(ctx context.Context, g *globals.Context, uid gregor1.UID,
    18  	convID chat1.ConversationID, messageID chat1.MessageID,
    19  	parentDir string, useArbitraryName bool) (string, io.WriteCloser, error) {
    20  	var sink io.WriteCloser
    21  	var err error
    22  	const openFlag int = os.O_RDWR | os.O_CREATE | os.O_TRUNC
    23  	if err := os.MkdirAll(parentDir, libkb.PermDir); err != nil {
    24  		return "", nil, err
    25  	}
    26  
    27  	reason := chat1.GetThreadReason_GENERAL
    28  	unboxed, err := g.ChatHelper.GetMessage(ctx, uid, convID,
    29  		messageID, true, &reason)
    30  	if err != nil {
    31  		return "", nil, err
    32  	}
    33  	if !unboxed.IsValid() {
    34  		return "", nil, errors.New("unable to download attachment from invalid message")
    35  	}
    36  	body := unboxed.Valid().MessageBody
    37  	typ, err := body.MessageType()
    38  	if err != nil || typ != chat1.MessageType_ATTACHMENT {
    39  		return "", nil, fmt.Errorf("invalid message type for download: %v", typ)
    40  	}
    41  	unsafeBasename := body.Attachment().Object.Filename
    42  	safeBasename := libkb.GetSafeFilename(unsafeBasename)
    43  
    44  	filePath, err := libkb.FindFilePathWithNumberSuffix(parentDir, safeBasename, useArbitraryName)
    45  	if err != nil {
    46  		return "", nil, err
    47  	}
    48  	if sink, err = os.OpenFile(filePath, openFlag, libkb.PermFile); err != nil {
    49  		return "", nil, err
    50  	}
    51  	return filePath, sink, nil
    52  }
    53  
    54  func Download(ctx context.Context, g *globals.Context, uid gregor1.UID,
    55  	convID chat1.ConversationID, messageID chat1.MessageID, sink io.WriteCloser, showPreview bool,
    56  	progress func(int64, int64), ri func() chat1.RemoteInterface) (err error) {
    57  
    58  	obj, err := AssetFromMessage(ctx, g, uid, convID, messageID, showPreview)
    59  	if err != nil {
    60  		return err
    61  	}
    62  	record := rpc.NewNetworkInstrumenter(g.ExternalG().RemoteNetworkInstrumenterStorage, "ChatAttachmentDownload")
    63  	defer func() { _ = record.RecordAndFinish(ctx, obj.Size) }()
    64  	fetcher := g.AttachmentURLSrv.GetAttachmentFetcher()
    65  	if err = fetcher.FetchAttachment(ctx, sink, convID, obj, ri, NewS3Signer(ri), progress); err != nil {
    66  		sink.Close()
    67  		return err
    68  	}
    69  	if err = sink.Close(); err != nil {
    70  		return err
    71  	}
    72  	return nil
    73  }