Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Track the full paths of attachments in the Attachment object #71

Merged
merged 1 commit into from
Nov 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions chatdb/attachment.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
type Attachment struct {
ID int
Filename string
Filepath string
MIMEType string
TransferName string
}
Expand Down
13 changes: 9 additions & 4 deletions internal/bagoup/bagoup.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,17 @@ func (cfg *configuration) validatePaths() error {
if err := cfg.OS.FileAccess(cfg.Options.DBPath); err != nil {
return errors.Wrapf(err, "test DB file %q - FIX: %s", cfg.Options.DBPath, _readmeURL)
}
if ok, err := cfg.OS.FileExist(cfg.Options.ExportPath); err != nil {
return errors.Wrapf(err, "check export path %q", cfg.Options.ExportPath)
var err error
var exportPathAbs string
if exportPathAbs, err = filepath.Abs(cfg.Options.ExportPath); err != nil {
return errors.Wrapf(err, "convert export path %q to an absolute path", cfg.Options.ExportPath)
}
cfg.Options.ExportPath = exportPathAbs
if ok, err := cfg.OS.FileExist(exportPathAbs); err != nil {
return errors.Wrapf(err, "check export path %q", exportPathAbs)
} else if ok {
return fmt.Errorf("export folder %q already exists - FIX: move it or specify a different export path with the --export-path option", cfg.Options.ExportPath)
return fmt.Errorf("export folder %q already exists - FIX: move it or specify a different export path with the --export-path option", exportPathAbs)
}
var err error
var attPathAbs string
if attPathAbs, err = filepath.Abs(cfg.Options.AttachmentsPath); err != nil {
return errors.Wrapf(err, "convert attachments path %q to an absolute path", cfg.Options.AttachmentsPath)
Expand Down
109 changes: 57 additions & 52 deletions internal/bagoup/bagoup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package bagoup

import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"
Expand All @@ -29,6 +30,10 @@ func TestBagoup(t *testing.T) {
SelfHandle: "Me",
AttachmentsPath: "/",
}
exportPathAbs := filepath.Join(wd, "messages-export")
logDirAbs := filepath.Join(exportPathAbs, ".bagoup")
logFileAbs := filepath.Join(logDirAbs, "out.log")
tildeexpansionAbs := filepath.Join(exportPathAbs, PreservedPathDir, PreservedPathTildeExpansionFile)
tenDotTwelve := "10.12"
tenDotTenDotTenDotTen := "10.10.10.10"
contactsPath := "contacts.vcf"
Expand All @@ -48,9 +53,9 @@ func TestBagoup(t *testing.T) {
setupMocks: func(osMock *mock_opsys.MockOS, dbMock *mock_chatdb.MockChatDB, ptMock *mock_pathtools.MockPathTools) {
gomock.InOrder(
osMock.EXPECT().FileAccess("~/Library/Messages/chat.db"),
osMock.EXPECT().FileExist("messages-export"),
osMock.EXPECT().MkdirAll("messages-export/.bagoup", os.ModePerm),
osMock.EXPECT().Create("messages-export/.bagoup/out.log").Return(devnull, nil),
osMock.EXPECT().FileExist(exportPathAbs),
osMock.EXPECT().MkdirAll(logDirAbs, os.ModePerm),
osMock.EXPECT().Create(logFileAbs).Return(devnull, nil),
osMock.EXPECT().GetMacOSVersion().Return(semver.MustParse("12.4"), nil),
dbMock.EXPECT().Init(semver.MustParse("12.4")),
dbMock.EXPECT().GetHandleMap(nil),
Expand All @@ -72,9 +77,9 @@ func TestBagoup(t *testing.T) {
gomock.InOrder(
osMock.EXPECT().ReadFile("testrelativepath/.tildeexpansion"),
osMock.EXPECT().FileAccess("~/Library/Messages/chat.db"),
osMock.EXPECT().FileExist("messages-export"),
osMock.EXPECT().MkdirAll("messages-export/.bagoup", os.ModePerm),
osMock.EXPECT().Create("messages-export/.bagoup/out.log").Return(devnull, nil),
osMock.EXPECT().FileExist(exportPathAbs),
osMock.EXPECT().MkdirAll(logDirAbs, os.ModePerm),
osMock.EXPECT().Create(logFileAbs).Return(devnull, nil),
osMock.EXPECT().GetMacOSVersion().Return(semver.MustParse("12.4"), nil),
dbMock.EXPECT().Init(semver.MustParse("12.4")),
dbMock.EXPECT().GetHandleMap(nil),
Expand Down Expand Up @@ -111,9 +116,9 @@ func TestBagoup(t *testing.T) {
setupMocks: func(osMock *mock_opsys.MockOS, _ *mock_chatdb.MockChatDB, _ *mock_pathtools.MockPathTools) {
gomock.InOrder(
osMock.EXPECT().FileAccess("~/Library/Messages/chat.db"),
osMock.EXPECT().FileExist("messages-export"),
osMock.EXPECT().MkdirAll("messages-export/.bagoup", os.ModePerm),
osMock.EXPECT().Create("messages-export/.bagoup/out.log").Return(devnull, nil),
osMock.EXPECT().FileExist(exportPathAbs),
osMock.EXPECT().MkdirAll(logDirAbs, os.ModePerm),
osMock.EXPECT().Create(logFileAbs).Return(devnull, nil),
osMock.EXPECT().GetMacOSVersion().Return(nil, errors.New("this is an exec error")),
)
},
Expand All @@ -125,30 +130,30 @@ func TestBagoup(t *testing.T) {
setupMocks: func(osMock *mock_opsys.MockOS, _ *mock_chatdb.MockChatDB, _ *mock_pathtools.MockPathTools) {
gomock.InOrder(
osMock.EXPECT().FileAccess("~/Library/Messages/chat.db"),
osMock.EXPECT().FileExist("messages-export").Return(true, nil),
osMock.EXPECT().FileExist(exportPathAbs).Return(true, nil),
)
},
wantErr: `export folder "messages-export" already exists - FIX: move it or specify a different export path with the --export-path option`,
wantErr: fmt.Sprintf(`export folder %q already exists - FIX: move it or specify a different export path with the --export-path option`, exportPathAbs),
},
{
msg: "error checking export path",
opts: defaultOpts,
setupMocks: func(osMock *mock_opsys.MockOS, _ *mock_chatdb.MockChatDB, _ *mock_pathtools.MockPathTools) {
gomock.InOrder(
osMock.EXPECT().FileAccess("~/Library/Messages/chat.db"),
osMock.EXPECT().FileExist("messages-export").Return(false, errors.New("this is a stat error")),
osMock.EXPECT().FileExist(exportPathAbs).Return(false, errors.New("this is a stat error")),
)
},
wantErr: `check export path "messages-export": this is a stat error`,
wantErr: fmt.Sprintf(`check export path %q: this is a stat error`, exportPathAbs),
},
{
msg: "error creating log directory",
opts: defaultOpts,
setupMocks: func(osMock *mock_opsys.MockOS, dbMock *mock_chatdb.MockChatDB, _ *mock_pathtools.MockPathTools) {
gomock.InOrder(
osMock.EXPECT().FileAccess("~/Library/Messages/chat.db"),
osMock.EXPECT().FileExist("messages-export"),
osMock.EXPECT().MkdirAll("messages-export/.bagoup", os.ModePerm).Return(errors.New("this is a permissions error")),
osMock.EXPECT().FileExist(exportPathAbs),
osMock.EXPECT().MkdirAll(logDirAbs, os.ModePerm).Return(errors.New("this is a permissions error")),
)
},
wantErr: "make log directory: this is a permissions error",
Expand All @@ -159,9 +164,9 @@ func TestBagoup(t *testing.T) {
setupMocks: func(osMock *mock_opsys.MockOS, dbMock *mock_chatdb.MockChatDB, _ *mock_pathtools.MockPathTools) {
gomock.InOrder(
osMock.EXPECT().FileAccess("~/Library/Messages/chat.db"),
osMock.EXPECT().FileExist("messages-export"),
osMock.EXPECT().MkdirAll("messages-export/.bagoup", os.ModePerm),
osMock.EXPECT().Create("messages-export/.bagoup/out.log").Return(devnull, errors.New("this is a permissions error")),
osMock.EXPECT().FileExist(exportPathAbs),
osMock.EXPECT().MkdirAll(logDirAbs, os.ModePerm),
osMock.EXPECT().Create(logFileAbs).Return(devnull, errors.New("this is a permissions error")),
)
},
wantErr: "create log file: this is a permissions error",
Expand All @@ -178,9 +183,9 @@ func TestBagoup(t *testing.T) {
setupMocks: func(osMock *mock_opsys.MockOS, dbMock *mock_chatdb.MockChatDB, ptMock *mock_pathtools.MockPathTools) {
gomock.InOrder(
osMock.EXPECT().FileAccess("~/Library/Messages/chat.db"),
osMock.EXPECT().FileExist("messages-export"),
osMock.EXPECT().MkdirAll("messages-export/.bagoup", os.ModePerm),
osMock.EXPECT().Create("messages-export/.bagoup/out.log").Return(devnull, nil),
osMock.EXPECT().FileExist(exportPathAbs),
osMock.EXPECT().MkdirAll(logDirAbs, os.ModePerm),
osMock.EXPECT().Create(logFileAbs).Return(devnull, nil),
dbMock.EXPECT().Init(semver.MustParse("10.12")),
dbMock.EXPECT().GetHandleMap(nil),
dbMock.EXPECT().GetAttachmentPaths(ptMock),
Expand All @@ -201,9 +206,9 @@ func TestBagoup(t *testing.T) {
setupMocks: func(osMock *mock_opsys.MockOS, _ *mock_chatdb.MockChatDB, _ *mock_pathtools.MockPathTools) {
gomock.InOrder(
osMock.EXPECT().FileAccess("~/Library/Messages/chat.db"),
osMock.EXPECT().FileExist("messages-export"),
osMock.EXPECT().MkdirAll("messages-export/.bagoup", os.ModePerm),
osMock.EXPECT().Create("messages-export/.bagoup/out.log").Return(devnull, nil),
osMock.EXPECT().FileExist(exportPathAbs),
osMock.EXPECT().MkdirAll(logDirAbs, os.ModePerm),
osMock.EXPECT().Create(logFileAbs).Return(devnull, nil),
)
},
wantErr: `parse Mac OS version "10.10.10.10": Invalid Semantic Version`,
Expand All @@ -220,9 +225,9 @@ func TestBagoup(t *testing.T) {
setupMocks: func(osMock *mock_opsys.MockOS, dbMock *mock_chatdb.MockChatDB, ptMock *mock_pathtools.MockPathTools) {
gomock.InOrder(
osMock.EXPECT().FileAccess("~/Library/Messages/chat.db"),
osMock.EXPECT().FileExist("messages-export"),
osMock.EXPECT().MkdirAll("messages-export/.bagoup", os.ModePerm),
osMock.EXPECT().Create("messages-export/.bagoup/out.log").Return(devnull, nil),
osMock.EXPECT().FileExist(exportPathAbs),
osMock.EXPECT().MkdirAll(logDirAbs, os.ModePerm),
osMock.EXPECT().Create(logFileAbs).Return(devnull, nil),
osMock.EXPECT().GetMacOSVersion().Return(semver.MustParse("12.4"), nil),
osMock.EXPECT().GetContactMap("contacts.vcf"),
dbMock.EXPECT().Init(semver.MustParse("12.4")),
Expand All @@ -245,9 +250,9 @@ func TestBagoup(t *testing.T) {
setupMocks: func(osMock *mock_opsys.MockOS, _ *mock_chatdb.MockChatDB, _ *mock_pathtools.MockPathTools) {
gomock.InOrder(
osMock.EXPECT().FileAccess("~/Library/Messages/chat.db"),
osMock.EXPECT().FileExist("messages-export"),
osMock.EXPECT().MkdirAll("messages-export/.bagoup", os.ModePerm),
osMock.EXPECT().Create("messages-export/.bagoup/out.log").Return(devnull, nil),
osMock.EXPECT().FileExist(exportPathAbs),
osMock.EXPECT().MkdirAll(logDirAbs, os.ModePerm),
osMock.EXPECT().Create(logFileAbs).Return(devnull, nil),
osMock.EXPECT().GetMacOSVersion().Return(semver.MustParse("12.4"), nil),
osMock.EXPECT().GetContactMap("contacts.vcf").Return(nil, errors.New("this is an os error")),
)
Expand All @@ -260,9 +265,9 @@ func TestBagoup(t *testing.T) {
setupMocks: func(osMock *mock_opsys.MockOS, dbMock *mock_chatdb.MockChatDB, _ *mock_pathtools.MockPathTools) {
gomock.InOrder(
osMock.EXPECT().FileAccess("~/Library/Messages/chat.db"),
osMock.EXPECT().FileExist("messages-export"),
osMock.EXPECT().MkdirAll("messages-export/.bagoup", os.ModePerm),
osMock.EXPECT().Create("messages-export/.bagoup/out.log").Return(devnull, nil),
osMock.EXPECT().FileExist(exportPathAbs),
osMock.EXPECT().MkdirAll(logDirAbs, os.ModePerm),
osMock.EXPECT().Create(logFileAbs).Return(devnull, nil),
osMock.EXPECT().GetMacOSVersion().Return(semver.MustParse("12.4"), nil),
dbMock.EXPECT().Init(semver.MustParse("12.4")).Return(errors.New("this is a DB error")),
)
Expand All @@ -275,9 +280,9 @@ func TestBagoup(t *testing.T) {
setupMocks: func(osMock *mock_opsys.MockOS, dbMock *mock_chatdb.MockChatDB, _ *mock_pathtools.MockPathTools) {
gomock.InOrder(
osMock.EXPECT().FileAccess("~/Library/Messages/chat.db"),
osMock.EXPECT().FileExist("messages-export"),
osMock.EXPECT().MkdirAll("messages-export/.bagoup", os.ModePerm),
osMock.EXPECT().Create("messages-export/.bagoup/out.log").Return(devnull, nil),
osMock.EXPECT().FileExist(exportPathAbs),
osMock.EXPECT().MkdirAll(logDirAbs, os.ModePerm),
osMock.EXPECT().Create(logFileAbs).Return(devnull, nil),
osMock.EXPECT().GetMacOSVersion().Return(semver.MustParse("12.4"), nil),
dbMock.EXPECT().Init(semver.MustParse("12.4")),
dbMock.EXPECT().GetHandleMap(nil).Return(nil, errors.New("this is a DB error")),
Expand All @@ -291,9 +296,9 @@ func TestBagoup(t *testing.T) {
setupMocks: func(osMock *mock_opsys.MockOS, dbMock *mock_chatdb.MockChatDB, ptMock *mock_pathtools.MockPathTools) {
gomock.InOrder(
osMock.EXPECT().FileAccess("~/Library/Messages/chat.db"),
osMock.EXPECT().FileExist("messages-export"),
osMock.EXPECT().MkdirAll("messages-export/.bagoup", os.ModePerm),
osMock.EXPECT().Create("messages-export/.bagoup/out.log").Return(devnull, nil),
osMock.EXPECT().FileExist(exportPathAbs),
osMock.EXPECT().MkdirAll(logDirAbs, os.ModePerm),
osMock.EXPECT().Create(logFileAbs).Return(devnull, nil),
osMock.EXPECT().GetMacOSVersion().Return(semver.MustParse("12.4"), nil),
dbMock.EXPECT().Init(semver.MustParse("12.4")),
dbMock.EXPECT().GetHandleMap(nil),
Expand All @@ -317,16 +322,16 @@ func TestBagoup(t *testing.T) {
setupMocks: func(osMock *mock_opsys.MockOS, dbMock *mock_chatdb.MockChatDB, ptMock *mock_pathtools.MockPathTools) {
gomock.InOrder(
osMock.EXPECT().FileAccess("~/Library/Messages/chat.db"),
osMock.EXPECT().FileExist("messages-export"),
osMock.EXPECT().MkdirAll("messages-export/.bagoup", os.ModePerm),
osMock.EXPECT().Create("messages-export/.bagoup/out.log").Return(devnull, nil),
osMock.EXPECT().FileExist(exportPathAbs),
osMock.EXPECT().MkdirAll(logDirAbs, os.ModePerm),
osMock.EXPECT().Create(logFileAbs).Return(devnull, nil),
osMock.EXPECT().GetMacOSVersion().Return(semver.MustParse("12.4"), nil),
dbMock.EXPECT().Init(semver.MustParse("12.4")),
dbMock.EXPECT().GetHandleMap(nil),
dbMock.EXPECT().GetAttachmentPaths(ptMock),
dbMock.EXPECT().GetChats(nil),
ptMock.EXPECT().GetHomeDir(),
osMock.EXPECT().Create("messages-export/bagoup-attachments/.tildeexpansion").Return(afero.NewMemMapFs().Create("dummy")),
osMock.EXPECT().Create(tildeexpansionAbs).Return(afero.NewMemMapFs().Create("dummy")),
osMock.EXPECT().RmTempDir().Times(2),
)
},
Expand All @@ -344,16 +349,16 @@ func TestBagoup(t *testing.T) {
setupMocks: func(osMock *mock_opsys.MockOS, dbMock *mock_chatdb.MockChatDB, ptMock *mock_pathtools.MockPathTools) {
gomock.InOrder(
osMock.EXPECT().FileAccess("~/Library/Messages/chat.db"),
osMock.EXPECT().FileExist("messages-export"),
osMock.EXPECT().MkdirAll("messages-export/.bagoup", os.ModePerm),
osMock.EXPECT().Create("messages-export/.bagoup/out.log").Return(devnull, nil),
osMock.EXPECT().FileExist(exportPathAbs),
osMock.EXPECT().MkdirAll(logDirAbs, os.ModePerm),
osMock.EXPECT().Create(logFileAbs).Return(devnull, nil),
osMock.EXPECT().GetMacOSVersion().Return(semver.MustParse("12.4"), nil),
dbMock.EXPECT().Init(semver.MustParse("12.4")),
dbMock.EXPECT().GetHandleMap(nil),
dbMock.EXPECT().GetAttachmentPaths(ptMock),
dbMock.EXPECT().GetChats(nil),
ptMock.EXPECT().GetHomeDir(),
osMock.EXPECT().Create("messages-export/bagoup-attachments/.tildeexpansion").Return(nil, errors.New("this is a permissions error")),
osMock.EXPECT().Create(tildeexpansionAbs).Return(nil, errors.New("this is a permissions error")),
osMock.EXPECT().RmTempDir(),
)
},
Expand All @@ -376,16 +381,16 @@ func TestBagoup(t *testing.T) {
rofs := afero.NewReadOnlyFs(rwfs)
gomock.InOrder(
osMock.EXPECT().FileAccess("~/Library/Messages/chat.db"),
osMock.EXPECT().FileExist("messages-export"),
osMock.EXPECT().MkdirAll("messages-export/.bagoup", os.ModePerm),
osMock.EXPECT().Create("messages-export/.bagoup/out.log").Return(devnull, nil),
osMock.EXPECT().FileExist(exportPathAbs),
osMock.EXPECT().MkdirAll(logDirAbs, os.ModePerm),
osMock.EXPECT().Create(logFileAbs).Return(devnull, nil),
osMock.EXPECT().GetMacOSVersion().Return(semver.MustParse("12.4"), nil),
dbMock.EXPECT().Init(semver.MustParse("12.4")),
dbMock.EXPECT().GetHandleMap(nil),
dbMock.EXPECT().GetAttachmentPaths(ptMock),
dbMock.EXPECT().GetChats(nil),
ptMock.EXPECT().GetHomeDir(),
osMock.EXPECT().Create("messages-export/bagoup-attachments/.tildeexpansion").Return(rofs.Open("dummy")),
osMock.EXPECT().Create(tildeexpansionAbs).Return(rofs.Open("dummy")),
osMock.EXPECT().RmTempDir(),
)
},
Expand All @@ -407,7 +412,7 @@ func TestBagoup(t *testing.T) {
osMock,
dbMock,
ptMock,
"messages-export/.bagoup",
logDirAbs,
time.Now(),
"",
)
Expand Down
35 changes: 16 additions & 19 deletions internal/bagoup/write.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,16 +147,16 @@ func (cfg *configuration) handleAttachments(outFile opsys.OutFile, msgID int, at
return nil
}
for _, att := range msgPaths {
attPath, mimeType, transferName := att.Filename, att.MIMEType, att.TransferName
err := cfg.validateAttachmentPath(attPath)
att.Filepath = filepath.Join(cfg.Options.AttachmentsPath, att.Filename)
err := cfg.validateAttachmentPath(att)
if _, ok := err.(errorMissingAttachment); ok {
// Attachment is missing. Just reference it, and skip copying/embedding.
cfg.counts.attachmentsMissing++
log.Printf("WARN: chat file %q - message %d - %s attachment %q (ID %d) - %s", outFile.Name(), msgID, mimeType, transferName, att.ID, err)
if err := outFile.ReferenceAttachment(transferName); err != nil {
return errors.Wrapf(err, "reference attachment %q", transferName)
log.Printf("WARN: chat file %q - message %d - %s attachment %q (ID %d) - %s", outFile.Name(), msgID, att.MIMEType, att.TransferName, att.ID, err)
if err := outFile.ReferenceAttachment(att.TransferName); err != nil {
return errors.Wrapf(err, "reference attachment %q", att.TransferName)
}
cfg.counts.attachments[mimeType]++
cfg.counts.attachments[att.MIMEType]++
continue
} else if err != nil {
return err
Expand All @@ -175,13 +175,12 @@ type errorMissingAttachment struct{ err error }

func (e errorMissingAttachment) Error() string { return e.err.Error() }

func (cfg configuration) validateAttachmentPath(attPath string) error {
if attPath == "" {
func (cfg configuration) validateAttachmentPath(att chatdb.Attachment) error {
if att.Filename == "" {
return errorMissingAttachment{err: errors.New("attachment has no local filename")}
}
attPath = filepath.Join(cfg.Options.AttachmentsPath, attPath)
if ok, err := cfg.OS.FileExist(attPath); err != nil {
return errors.Wrapf(err, "check existence of file %q - POSSIBLE FIX: %s", attPath, _readmeURL)
if ok, err := cfg.OS.FileExist(att.Filepath); err != nil {
return errors.Wrapf(err, "check existence of file %q - POSSIBLE FIX: %s", att.Filepath, _readmeURL)
} else if !ok {
return errorMissingAttachment{err: errors.New("attachment does not exist locally")}
}
Expand All @@ -192,27 +191,25 @@ func (cfg *configuration) copyAttachment(att *chatdb.Attachment, attDir string)
if !cfg.Options.CopyAttachments {
return nil
}
attPath, mimeType := att.Filename, att.MIMEType
unique := true
if cfg.Options.PreservePaths {
unique = false
attDir = filepath.Join(cfg.Options.ExportPath, PreservedPathDir, filepath.Dir(attPath))
attDir = filepath.Join(cfg.Options.ExportPath, PreservedPathDir, filepath.Dir(att.Filename))
if err := cfg.OS.MkdirAll(attDir, os.ModePerm); err != nil {
return errors.Wrapf(err, "create directory %q", attDir)
}
}
attPath = filepath.Join(cfg.Options.AttachmentsPath, attPath)
dstPath, err := cfg.OS.CopyFile(attPath, attDir, unique)
dstPath, err := cfg.OS.CopyFile(att.Filepath, attDir, unique)
if err != nil {
return errors.Wrapf(err, "copy attachment %q to %q", attPath, attDir)
return errors.Wrapf(err, "copy attachment %q to %q", att.Filepath, attDir)
}
att.Filename = dstPath
cfg.counts.attachmentsCopied[mimeType]++
att.Filepath = dstPath
cfg.counts.attachmentsCopied[att.MIMEType]++
return nil
}

func (cfg *configuration) writeAttachment(outFile opsys.OutFile, att chatdb.Attachment) error {
attPath, mimeType := filepath.Join(cfg.Options.AttachmentsPath, att.Filename), att.MIMEType
attPath, mimeType := att.Filepath, att.MIMEType
if cfg.Options.OutputPDF {
if jpgPath, err := cfg.OS.HEIC2JPG(attPath); err != nil {
cfg.counts.conversionsFailed++
Expand Down
Loading
Loading