Store base64 files locally before sending.

This commit is contained in:
zeetabit
2022-08-04 10:35:50 +02:00
parent 34e7db3c67
commit 98a130dd05
3 changed files with 132 additions and 56 deletions

View File

@@ -1,23 +1,33 @@
package client package client
import ( import (
"encoding/base64"
"errors"
"os"
"reflect" "reflect"
"strings" "strings"
"github.com/gabriel-vasile/mimetype"
uuid "github.com/gofrs/uuid"
) )
type AttachmentEntry struct { type AttachmentEntry struct {
MimeInfo string MimeInfo string
FileName string FileName string
DirName string
Base64 string Base64 string
FilePath string FilePath string
attachmentTmpDir string
} }
func NewAttachmentEntry(attachmentData string) *AttachmentEntry { func NewAttachmentEntry(attachmentData string, attachmentTmpDir string) *AttachmentEntry {
attachmentEntry := AttachmentEntry{ attachmentEntry := AttachmentEntry{
MimeInfo: "", MimeInfo: "",
FileName: "", FileName: "",
DirName: "",
Base64: "", Base64: "",
FilePath: "", FilePath: "",
attachmentTmpDir: attachmentTmpDir,
} }
attachmentEntry.extractMetaData(attachmentData) attachmentEntry.extractMetaData(attachmentData)
@@ -52,6 +62,70 @@ func (attachmentEntry *AttachmentEntry) extractMetaData(attachmentData string) {
} }
} }
func (attachmentEntry *AttachmentEntry) storeBase64AsTemporaryFile() error {
if strings.Compare(attachmentEntry.Base64, "") == 0 {
return errors.New("The base64 data does not exist.")
}
dec, err := base64.StdEncoding.DecodeString(attachmentEntry.Base64)
if err != nil {
return err
}
// if no custom filename
if strings.Compare(attachmentEntry.FileName, "") == 0 {
mimeType := mimetype.Detect(dec)
fileNameUuid, err := uuid.NewV4()
if err != nil {
return err
}
attachmentEntry.FileName = fileNameUuid.String() + mimeType.Extension()
}
dirNameUuid, err := uuid.NewV4()
if err != nil {
return err
}
attachmentEntry.DirName = dirNameUuid.String()
dirPath := attachmentEntry.attachmentTmpDir + attachmentEntry.DirName
if err := os.Mkdir(dirPath, os.ModePerm); err != nil {
return err
}
attachmentEntry.FilePath = dirPath + string(os.PathSeparator) + attachmentEntry.FileName
f, err := os.Create(attachmentEntry.FilePath)
if err != nil {
return err
}
defer f.Close()
if _, err := f.Write(dec); err != nil {
attachmentEntry.cleanUp()
return err
}
if err := f.Sync(); err != nil {
attachmentEntry.cleanUp()
return err
}
f.Close()
return nil
}
func (attachmentEntry *AttachmentEntry) cleanUp() {
if strings.Compare(attachmentEntry.FilePath, "") != 0 {
os.Remove(attachmentEntry.FilePath)
}
if strings.Compare(attachmentEntry.DirName, "") != 0 {
dirPath := attachmentEntry.attachmentTmpDir + attachmentEntry.DirName
os.Remove(dirPath)
}
}
func (attachmentEntry *AttachmentEntry) setFieldValueByName(fieldName string, fieldValue string) { func (attachmentEntry *AttachmentEntry) setFieldValueByName(fieldName string, fieldValue string) {
reflectPointer := reflect.ValueOf(attachmentEntry) reflectPointer := reflect.ValueOf(attachmentEntry)
reflectStructure := reflectPointer.Elem() reflectStructure := reflectPointer.Elem()

View File

@@ -1,6 +1,8 @@
package client package client
import ( import (
"flag"
"os"
"strings" "strings"
"testing" "testing"
) )
@@ -11,42 +13,45 @@ func Test_Attachment_ExtractMetadata_ShouldPrepareDataFor_toDataForSignal(t *tes
inputData string inputData string
resultIsWithMetaData bool resultIsWithMetaData bool
base64Expected string base64Expected string
base64Valid bool
fileNameExpected string fileNameExpected string
mimeInfoExpected string mimeInfoExpected string
toDataForSignal string toDataForSignal string
}{ }{
{ {
"BC base64 - compatibility", "MTIzNDU=", false, "MTIzNDU=", "", "", "MTIzNDU=", "BC base64 - compatibility", "MTIzNDU=", false, "MTIzNDU=", true, "", "", "MTIzNDU=",
}, },
{ {
"+base64 -data -filename", "base64,MTIzNDU=", false, "base64,MTIzNDU=", "", "", "base64,MTIzNDU=", "+base64 -data -filename", "base64,MTIzNDU=", false, "base64,MTIzNDU=", false, "", "", "base64,MTIzNDU=",
}, },
{ {
"+base64 +data -filename", "data:someData;base64,MTIzNDU=", true, "MTIzNDU=", "", "someData", "data:someData;base64,MTIzNDU=", "+base64 +data -filename", "data:someData;base64,MTIzNDU=", true, "MTIzNDU=", true, "", "someData", "data:someData;base64,MTIzNDU=",
}, },
{ {
"+base64 -data +filename", "filename=file.name;base64,MTIzNDU=", false, "filename=file.name;base64,MTIzNDU=", "", "", "filename=file.name;base64,MTIzNDU=", "+base64 -data +filename", "filename=file.name;base64,MTIzNDU=", false, "filename=file.name;base64,MTIzNDU=", false, "", "", "filename=file.name;base64,MTIzNDU=",
}, },
{ {
"+base64 +data +filename", "data:someData;filename=file.name;base64,MTIzNDU=", true, "MTIzNDU=", "file.name", "someData", "data:someData;filename=file.name;base64,MTIzNDU=", "+base64 +data +filename", "data:someData;filename=file.name;base64,MTIzNDU=", true, "MTIzNDU=", true, "file.name", "someData", "data:someData;filename=file.name;base64,MTIzNDU=",
}, },
{ {
"-base64 -data -filename", "INVALIDMTIzNDU=", false, "INVALIDMTIzNDU=", "", "", "INVALIDMTIzNDU=", "-base64 -data -filename", "INVALIDMTIzNDU=", false, "INVALIDMTIzNDU=", false, "", "", "INVALIDMTIzNDU=",
}, },
{ {
"-base64 +data -filename", "data:someData;INVALIDMTIzNDU=", false, "data:someData;INVALIDMTIzNDU=", "", "", "data:someData;INVALIDMTIzNDU=", "-base64 +data -filename", "data:someData;INVALIDMTIzNDU=", false, "data:someData;INVALIDMTIzNDU=", false, "", "", "data:someData;INVALIDMTIzNDU=",
}, },
{ {
"-base64 -data +filename", "filename=file.name;INVALIDMTIzNDU=", false, "filename=file.name;INVALIDMTIzNDU=", "", "", "filename=file.name;INVALIDMTIzNDU=", "-base64 -data +filename", "filename=file.name;INVALIDMTIzNDU=", false, "filename=file.name;INVALIDMTIzNDU=", false, "", "", "filename=file.name;INVALIDMTIzNDU=",
}, },
{ {
"-base64 +data +filename", "data:someData;filename=file.name;INVALIDMTIzNDU=", false, "data:someData;filename=file.name;INVALIDMTIzNDU=", "", "", "data:someData;filename=file.name;INVALIDMTIzNDU=", "-base64 +data +filename", "data:someData;filename=file.name;INVALIDMTIzNDU=", false, "data:someData;filename=file.name;INVALIDMTIzNDU=", false, "", "", "data:someData;filename=file.name;INVALIDMTIzNDU=",
}, },
} }
attachmentTmp := flag.String("attachment-tmp-dir", string(os.PathSeparator)+"tmp"+string(os.PathSeparator), "Attachment tmp directory")
for _, tt := range testCases { for _, tt := range testCases {
t.Run(tt.nameTest, func(t *testing.T) { t.Run(tt.nameTest, func(t *testing.T) {
attachmentEntry := NewAttachmentEntry(tt.inputData) attachmentEntry := NewAttachmentEntry(tt.inputData, *attachmentTmp)
if attachmentEntry.isWithMetaData() != tt.resultIsWithMetaData { if attachmentEntry.isWithMetaData() != tt.resultIsWithMetaData {
t.Errorf("isWithMetaData() got \"%v\", want \"%v\"", attachmentEntry.isWithMetaData(), tt.resultIsWithMetaData) t.Errorf("isWithMetaData() got \"%v\", want \"%v\"", attachmentEntry.isWithMetaData(), tt.resultIsWithMetaData)
@@ -67,6 +72,36 @@ func Test_Attachment_ExtractMetadata_ShouldPrepareDataFor_toDataForSignal(t *tes
if strings.Compare(attachmentEntry.toDataForSignal(), tt.toDataForSignal) != 0 { if strings.Compare(attachmentEntry.toDataForSignal(), tt.toDataForSignal) != 0 {
t.Errorf("toDataForSignal() got \"%v\", want \"%v\"", attachmentEntry.toDataForSignal(), tt.toDataForSignal) t.Errorf("toDataForSignal() got \"%v\", want \"%v\"", attachmentEntry.toDataForSignal(), tt.toDataForSignal)
} }
err := attachmentEntry.storeBase64AsTemporaryFile()
if err != nil && tt.base64Valid {
t.Error("storeBase64AsTemporaryFile error: %w", err)
return
}
info, err := os.Stat(attachmentEntry.FilePath)
if os.IsNotExist(err) && tt.base64Valid {
t.Error("file not exists after storeBase64AsTemporaryFile: %w", err)
return
}
if (info == nil || info.IsDir()) && tt.base64Valid {
t.Error("is not a file by path after storeBase64AsTemporaryFile")
t.Error(attachmentEntry)
return
}
attachmentEntry.cleanUp()
info, err = os.Stat(attachmentEntry.FilePath)
if info != nil && !os.IsNotExist(err) && tt.base64Valid {
t.Error("no info or file exists after cleanUp")
return
}
info, err = os.Stat(*attachmentTmp + attachmentEntry.DirName)
if info != nil && !os.IsNotExist(err) && tt.base64Valid {
t.Error("dir exists after cleanUp")
return
}
}) })
} }
} }

View File

@@ -12,7 +12,6 @@ import (
"strings" "strings"
securejoin "github.com/cyphar/filepath-securejoin" securejoin "github.com/cyphar/filepath-securejoin"
"github.com/gabriel-vasile/mimetype"
"github.com/h2non/filetype" "github.com/h2non/filetype"
uuid "github.com/gofrs/uuid" uuid "github.com/gofrs/uuid"
@@ -136,11 +135,7 @@ func cleanupTmpFiles(paths []string) {
func cleanupAttachmentEntries(attachmentEntries []AttachmentEntry) { func cleanupAttachmentEntries(attachmentEntries []AttachmentEntry) {
for _, attachmentEntry := range attachmentEntries { for _, attachmentEntry := range attachmentEntries {
if len(attachmentEntry.FilePath) == 0 { attachmentEntry.cleanUp()
continue
}
os.Remove(attachmentEntry.FilePath)
} }
} }
@@ -311,43 +306,15 @@ func (s *SignalClient) send(number string, message string,
attachmentEntries := []AttachmentEntry{} attachmentEntries := []AttachmentEntry{}
for _, base64Attachment := range base64Attachments { for _, base64Attachment := range base64Attachments {
attachmentEntry := NewAttachmentEntry(base64Attachment) attachmentEntry := NewAttachmentEntry(base64Attachment, s.attachmentTmpDir)
u, err := uuid.NewV4() err := attachmentEntry.storeBase64AsTemporaryFile()
if err != nil { if err != nil {
return nil, err
}
dec, err := base64.StdEncoding.DecodeString(attachmentEntry.Base64)
if err != nil {
return nil, err
}
mimeType := mimetype.Detect(dec)
if attachmentEntry.isWithMetaData() {
attachmentEntries = append(attachmentEntries, *attachmentEntry)
continue
}
attachmentEntry.FilePath = s.attachmentTmpDir + u.String() + mimeType.Extension()
attachmentEntries = append(attachmentEntries, *attachmentEntry)
f, err := os.Create(attachmentEntry.FilePath)
if err != nil {
return nil, err
}
defer f.Close()
if _, err := f.Write(dec); err != nil {
cleanupAttachmentEntries(attachmentEntries)
return nil, err
}
if err := f.Sync(); err != nil {
cleanupAttachmentEntries(attachmentEntries) cleanupAttachmentEntries(attachmentEntries)
return nil, err return nil, err
} }
f.Close() attachmentEntries = append(attachmentEntries, *attachmentEntry)
} }
if s.signalCliMode == JsonRpc { if s.signalCliMode == JsonRpc {