From 86f9b32870d2127f3fd3e196cecfc24265cf8d87 Mon Sep 17 00:00:00 2001 From: David Tagatac Date: Tue, 26 Jul 2022 12:46:01 -0600 Subject: [PATCH] Add attachments copied to the results string --- bagoup.go | 47 ++++++++++++++++++++++++----------------------- bagoup_test.go | 8 ++++---- export.go | 8 ++++---- export_test.go | 4 ++-- main.go | 29 +++++++++++++++++------------ write.go | 40 +++++++++++++++++++++------------------- write_test.go | 19 ++++++++++--------- 7 files changed, 82 insertions(+), 73 deletions(-) diff --git a/bagoup.go b/bagoup.go index 8259a6c..0ef52cf 100644 --- a/bagoup.go +++ b/bagoup.go @@ -18,7 +18,7 @@ import ( ) func (cfg configuration) bagoup() error { - opts := cfg.Options + opts := cfg.opts if err := validatePaths(cfg.OS, opts); err != nil { return err } @@ -34,11 +34,11 @@ func (cfg configuration) bagoup() error { log.SetOutput(io.MultiWriter(logFile, os.Stdout)) if opts.MacOSVersion != nil { - cfg.MacOSVersion, err = semver.NewVersion(*opts.MacOSVersion) + cfg.macOSVersion, err = semver.NewVersion(*opts.MacOSVersion) if err != nil { return errors.Wrapf(err, "parse Mac OS version %q", *opts.MacOSVersion) } - } else if cfg.MacOSVersion, err = cfg.OS.GetMacOSVersion(); err != nil { + } else if cfg.macOSVersion, err = cfg.OS.GetMacOSVersion(); err != nil { return errors.Wrap(err, "get Mac OS version - FIX: specify the Mac OS version from which chat.db was copied with the --mac-os-version option") } @@ -50,18 +50,18 @@ func (cfg configuration) bagoup() error { } } - if err := cfg.ChatDB.Init(cfg.MacOSVersion); err != nil { - return errors.Wrapf(err, "initialize the database for reading on Mac OS version %s", cfg.MacOSVersion.String()) + if err := cfg.ChatDB.Init(cfg.macOSVersion); err != nil { + return errors.Wrapf(err, "initialize the database for reading on Mac OS version %s", cfg.macOSVersion.String()) } - cfg.HandleMap, err = cfg.ChatDB.GetHandleMap(contactMap) + cfg.handleMap, err = cfg.ChatDB.GetHandleMap(contactMap) if err != nil { return errors.Wrap(err, "get handle map") } defer cfg.OS.RmTempDir() err = cfg.exportChats(contactMap) - printResults(opts.ExportPath, cfg.Counts, time.Since(cfg.StartTime)) + printResults(opts.ExportPath, cfg.counts, time.Since(cfg.startTime)) if err != nil { return errors.Wrap(err, "export chats") } @@ -81,26 +81,16 @@ func validatePaths(s opsys.OS, opts options) error { } func printResults(exportPath string, c counts, duration time.Duration) { - var attachmentsString string - for mimeType, count := range c.attachments { - attachmentsString += fmt.Sprintf("\n\t%s: %d", mimeType, count) - } - if attachmentsString == "" { - attachmentsString = "0" - } - var attachmentsEmbeddedString string - for mimeType, count := range c.attachmentsEmbedded { - attachmentsEmbeddedString += fmt.Sprintf("\n\t%s: %d", mimeType, count) - } - if attachmentsEmbeddedString == "" { - attachmentsEmbeddedString = "0" - } + attCpString := makeAttachmentsString(c.attachmentsCopied) + attString := makeAttachmentsString(c.attachments) + attEmbdString := makeAttachmentsString(c.attachmentsEmbedded) log.Printf(`%sBAGOUP RESULTS: bagoup version: %s Export folder: %q Export files written: %d Chats exported: %d Messages exported: %d +Attachments copied: %s Attachments referenced or embedded: %s Attachments embedded: %s Attachments missing (see warnings above): %d @@ -113,8 +103,9 @@ Time elapsed: %s%s`, c.files, c.chats, c.messages, - attachmentsString, - attachmentsEmbeddedString, + attCpString, + attString, + attEmbdString, c.attachmentsMissing, c.conversions, c.conversionsFailed, @@ -122,3 +113,13 @@ Time elapsed: %s%s`, "\x1b[0m", ) } + +func makeAttachmentsString(attCounts map[string]int) (attString string) { + attCount := 0 + for mimeType, count := range attCounts { + attCount += count + attString += fmt.Sprintf("\n\t%s: %d", mimeType, count) + } + attString = fmt.Sprintf("%d%s", attCount, attString) + return +} diff --git a/bagoup_test.go b/bagoup_test.go index 57c58b8..879d792 100644 --- a/bagoup_test.go +++ b/bagoup_test.go @@ -268,10 +268,10 @@ func TestBagoup(t *testing.T) { tt.setupMocks(osMock, dbMock) cfg := configuration{ - Options: tt.opts, - OS: osMock, - ChatDB: dbMock, - logDir: "messages-export/.bagoup", + opts: tt.opts, + OS: osMock, + ChatDB: dbMock, + logDir: "messages-export/.bagoup", } err := cfg.bagoup() if tt.wantErr != "" { diff --git a/export.go b/export.go index 81c2526..17bd350 100644 --- a/export.go +++ b/export.go @@ -31,8 +31,8 @@ func getAttachmentPaths(cfg *configuration) error { if err != nil { return errors.Wrap(err, "get attachment paths") } - cfg.AttachmentPaths = attPaths - if cfg.Options.OutputPDF || cfg.Options.CopyAttachments { + cfg.attachmentPaths = attPaths + if cfg.opts.OutputPDF || cfg.opts.CopyAttachments { for _, msgPaths := range attPaths { if len(msgPaths) == 0 { continue @@ -50,7 +50,7 @@ func getAttachmentPaths(cfg *configuration) error { } func (cfg *configuration) exportEntityChats(entityChats chatdb.EntityChats) error { - mergeChats := !cfg.Options.SeparateChats + mergeChats := !cfg.opts.SeparateChats var guids []string var entityMessageIDs []chatdb.DatedMessageID for _, chat := range entityChats.Chats { @@ -66,7 +66,7 @@ func (cfg *configuration) exportEntityChats(entityChats chatdb.EntityChats) erro return err } } - cfg.Counts.chats += 1 + cfg.counts.chats += 1 } if mergeChats { if err := cfg.writeFile(entityChats.Name, guids, entityMessageIDs); err != nil { diff --git a/export_test.go b/export_test.go index 2f86110..24d1cb7 100644 --- a/export_test.go +++ b/export_test.go @@ -307,7 +307,7 @@ func TestExportChats(t *testing.T) { attachmentsEmbedded: map[string]int{}, } cfg := configuration{ - Options: options{ + opts: options{ ExportPath: "messages-export", SeparateChats: tt.separateChats, OutputPDF: tt.pdf, @@ -315,7 +315,7 @@ func TestExportChats(t *testing.T) { }, OS: osMock, ChatDB: dbMock, - Counts: cnts, + counts: cnts, } err := cfg.exportChats(nil) if tt.wantErr != "" { diff --git a/main.go b/main.go index 26e53d1..24cf9ce 100644 --- a/main.go +++ b/main.go @@ -56,21 +56,22 @@ type ( PrintVersion bool `short:"v" long:"version" description:"Show the version of bagoup"` } configuration struct { - Options options + opts options opsys.OS chatdb.ChatDB logDir string - MacOSVersion *semver.Version - HandleMap map[int]string - AttachmentPaths map[int][]chatdb.Attachment - Counts counts - StartTime time.Time + macOSVersion *semver.Version + handleMap map[int]string + attachmentPaths map[int][]chatdb.Attachment + counts counts + startTime time.Time } counts struct { files int chats int messages int attachments map[string]int + attachmentsCopied map[string]int attachmentsEmbedded map[string]int attachmentsMissing int conversions int @@ -104,12 +105,16 @@ func main() { logDir := filepath.Join(opts.ExportPath, ".bagoup") cfg := configuration{ - Options: opts, - OS: s, - ChatDB: cdb, - logDir: logDir, - Counts: counts{attachments: map[string]int{}, attachmentsEmbedded: map[string]int{}}, - StartTime: startTime, + opts: opts, + OS: s, + ChatDB: cdb, + logDir: logDir, + counts: counts{ + attachments: map[string]int{}, + attachmentsCopied: map[string]int{}, + attachmentsEmbedded: map[string]int{}, + }, + startTime: startTime, } logFatalOnErr(cfg.bagoup()) logFatalOnErr(errors.Wrapf(db.Close(), "close DB file %q", dbPath)) diff --git a/write.go b/write.go index 08d23f2..a630085 100644 --- a/write.go +++ b/write.go @@ -16,19 +16,19 @@ import ( ) func (cfg *configuration) writeFile(entityName string, guids []string, messageIDs []chatdb.DatedMessageID) error { - chatDirPath := filepath.Join(cfg.Options.ExportPath, entityName) + chatDirPath := filepath.Join(cfg.opts.ExportPath, entityName) if err := cfg.OS.MkdirAll(chatDirPath, os.ModePerm); err != nil { return errors.Wrapf(err, "create directory %q", chatDirPath) } filename := strings.Join(guids, ";;;") chatPath := filepath.Join(chatDirPath, filename) - outFile, err := cfg.OS.NewOutFile(chatPath, cfg.Options.OutputPDF, cfg.Options.IncludePPA) + outFile, err := cfg.OS.NewOutFile(chatPath, cfg.opts.OutputPDF, cfg.opts.IncludePPA) if err != nil { return errors.Wrapf(err, "open/create file %q", chatPath) } defer outFile.Close() attDir := filepath.Join(chatDirPath, "attachments") - if cfg.Options.CopyAttachments && !cfg.Options.PreservePaths { + if cfg.opts.CopyAttachments && !cfg.opts.PreservePaths { if err := cfg.OS.Mkdir(attDir, os.ModePerm); err != nil { return errors.Wrapf(err, "create directory %q", attDir) } @@ -37,7 +37,7 @@ func (cfg *configuration) writeFile(entityName string, guids []string, messageID sort.SliceStable(messageIDs, func(i, j int) bool { return messageIDs[i].Date < messageIDs[j].Date }) msgCount := 0 for _, messageID := range messageIDs { - msg, err := cfg.ChatDB.GetMessage(messageID.ID, cfg.HandleMap) + msg, err := cfg.ChatDB.GetMessage(messageID.ID, cfg.handleMap) if err != nil { return errors.Wrapf(err, "get message with ID %d", messageID.ID) } @@ -61,13 +61,13 @@ func (cfg *configuration) writeFile(entityName string, guids []string, messageID if err := outFile.Close(); err != nil { return errors.Wrapf(err, "write/close chat file %q", outFile.Name()) } - cfg.Counts.files += 1 - cfg.Counts.messages += msgCount + cfg.counts.files += 1 + cfg.counts.messages += msgCount return nil } func (cfg *configuration) handleAttachments(outFile opsys.OutFile, msgID int, attDir string) error { - msgPaths, ok := cfg.AttachmentPaths[msgID] + msgPaths, ok := cfg.attachmentPaths[msgID] if !ok { return nil } @@ -76,17 +76,17 @@ func (cfg *configuration) handleAttachments(outFile opsys.OutFile, msgID int, at err := validateAttachmentPath(cfg.OS, attPath) if _, ok := err.(errorMissingAttachment); ok { // Attachment is missing. Just reference it, and skip copying/embedding. - cfg.Counts.attachmentsMissing += 1 + cfg.counts.attachmentsMissing += 1 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) } - cfg.Counts.attachments[mimeType] += 1 + cfg.counts.attachments[mimeType] += 1 continue } else if err != nil { return err } - if err := cfg.copyAttachment(attPath, attDir); err != nil { + if err := cfg.copyAttachment(att, attDir); err != nil { return err } if err := cfg.writeAttachment(outFile, att); err != nil { @@ -112,14 +112,15 @@ func validateAttachmentPath(s opsys.OS, attPath string) error { return nil } -func (cfg configuration) copyAttachment(attPath, attDir string) error { - if !cfg.Options.CopyAttachments { +func (cfg *configuration) copyAttachment(att chatdb.Attachment, attDir string) error { + if !cfg.opts.CopyAttachments { return nil } + attPath, mimeType := att.Filename, att.MIMEType unique := true - if cfg.Options.PreservePaths { + if cfg.opts.PreservePaths { unique = false - attDir = filepath.Join(cfg.Options.ExportPath, "bagoup-attachments", filepath.Dir(attPath)) + attDir = filepath.Join(cfg.opts.ExportPath, "bagoup-attachments", filepath.Dir(attPath)) if err := cfg.OS.MkdirAll(attDir, os.ModePerm); err != nil { return errors.Wrapf(err, "create directory %q", attDir) } @@ -127,17 +128,18 @@ func (cfg configuration) copyAttachment(attPath, attDir string) error { if err := cfg.OS.CopyFile(attPath, attDir, unique); err != nil { return errors.Wrapf(err, "copy attachment %q to %q", attPath, attDir) } + cfg.counts.attachmentsCopied[mimeType] += 1 return nil } func (cfg *configuration) writeAttachment(outFile opsys.OutFile, att chatdb.Attachment) error { attPath, mimeType := att.Filename, att.MIMEType - if cfg.Options.OutputPDF { + if cfg.opts.OutputPDF { if jpgPath, err := cfg.OS.HEIC2JPG(attPath); err != nil { - cfg.Counts.conversionsFailed += 1 + cfg.counts.conversionsFailed += 1 log.Printf("WARN: chat file %q - convert HEIC file %q to JPG: %s", outFile.Name(), attPath, err) } else if jpgPath != attPath { - cfg.Counts.conversions += 1 + cfg.counts.conversions += 1 attPath, mimeType = jpgPath, "image/jpeg" } } @@ -146,8 +148,8 @@ func (cfg *configuration) writeAttachment(outFile opsys.OutFile, att chatdb.Atta return errors.Wrapf(err, "include attachment %q", attPath) } if embedded { - cfg.Counts.attachmentsEmbedded[mimeType] += 1 + cfg.counts.attachmentsEmbedded[mimeType] += 1 } - cfg.Counts.attachments[mimeType] += 1 + cfg.counts.attachments[mimeType] += 1 return nil } diff --git a/write_test.go b/write_test.go index ae1f2a8..31c7996 100644 --- a/write_test.go +++ b/write_test.go @@ -460,10 +460,11 @@ func TestWriteFile(t *testing.T) { cnts := counts{ attachments: map[string]int{}, + attachmentsCopied: map[string]int{}, attachmentsEmbedded: map[string]int{}, } cfg := configuration{ - Options: options{ + opts: options{ ExportPath: "messages-export", OutputPDF: tt.pdf, CopyAttachments: tt.copyAttachments, @@ -471,15 +472,15 @@ func TestWriteFile(t *testing.T) { }, OS: osMock, ChatDB: dbMock, - MacOSVersion: semver.MustParse("12.4"), - AttachmentPaths: map[int][]chatdb.Attachment{ + macOSVersion: semver.MustParse("12.4"), + attachmentPaths: map[int][]chatdb.Attachment{ 2: { chatdb.Attachment{Filename: "attachment1.heic", MIMEType: "image/heic", TransferName: "att1transfer.heic"}, chatdb.Attachment{Filename: "attachment2.jpeg", MIMEType: "image/jpeg", TransferName: "att2transfer.jpeg"}, chatdb.Attachment{Filename: "", MIMEType: "image/png", TransferName: "att3transfer.png"}, }, }, - Counts: cnts, + counts: cnts, } err := cfg.writeFile( "friend", @@ -494,11 +495,11 @@ func TestWriteFile(t *testing.T) { return } assert.NilError(t, err) - assert.Equal(t, 2, cfg.Counts.messages) - assert.Equal(t, tt.wantJPGs, cfg.Counts.attachments["image/jpeg"]) - assert.Equal(t, tt.wantEmbedded, cfg.Counts.attachmentsEmbedded["image/jpeg"]) - assert.Equal(t, tt.wantConv, cfg.Counts.conversions) - assert.Equal(t, tt.wantConvFail, cfg.Counts.conversionsFailed) + assert.Equal(t, 2, cfg.counts.messages) + assert.Equal(t, tt.wantJPGs, cfg.counts.attachments["image/jpeg"]) + assert.Equal(t, tt.wantEmbedded, cfg.counts.attachmentsEmbedded["image/jpeg"]) + assert.Equal(t, tt.wantConv, cfg.counts.conversions) + assert.Equal(t, tt.wantConvFail, cfg.counts.conversionsFailed) }) } }