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 }