diff --git a/CHANGELOG.md b/CHANGELOG.md index 83bef4dc8..01f9cbaba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Handle the case where an email or event cannot be retrieved from Exchange due to an `ErrorCorruptData` error. Corso will skip over the item but report it in the backup summary. - Emails attached within other emails are now correctly exported +- Gracefully handle email and post attachments without name when exporting to eml ## [v0.19.0] (beta) - 2024-02-06 diff --git a/src/internal/converters/eml/eml.go b/src/internal/converters/eml/eml.go index 35810e5eb..35c3519e5 100644 --- a/src/internal/converters/eml/eml.go +++ b/src/internal/converters/eml/eml.go @@ -171,6 +171,13 @@ func getFileAttachment(ctx context.Context, attachment models.Attachmentable) (* } name := ptr.Val(attachment.GetName()) + if len(name) == 0 { + // Graph as of now does not let us create any attachments + // without a name, but we have run into instances where we have + // see attachments without a name, possibly from old + // data. This is for those cases. + name = "Unnamed" + } contentID, err := attachment.GetBackingStore().Get("contentId") if err != nil { @@ -472,6 +479,9 @@ func FromJSONPostToEML( } name := ptr.Val(attachment.GetName()) + if len(name) == 0 { + name = "Unnamed" + } contentID, err := attachment.GetBackingStore().Get("contentId") if err != nil { diff --git a/src/internal/converters/eml/eml_test.go b/src/internal/converters/eml/eml_test.go index fb95b8e55..f9ffbb9ca 100644 --- a/src/internal/converters/eml/eml_test.go +++ b/src/internal/converters/eml/eml_test.go @@ -162,6 +162,44 @@ func (suite *EMLUnitSuite) TestConvert_edge_cases() { require.NoError(suite.T(), err, "setting attachment content") }, }, + { + name: "attachment without name", + transform: func(msg models.Messageable) { + attachments := msg.GetAttachments() + attachments[1].SetName(ptr.To("")) + + // This test has to be run on a non inline attachment + // as inline attachments use contentID instead of name + // even when there is a name. + assert.False(suite.T(), ptr.Val(attachments[1].GetIsInline())) + }, + }, + { + name: "attachment with nil name", + transform: func(msg models.Messageable) { + attachments := msg.GetAttachments() + attachments[1].SetName(nil) + + // This test has to be run on a non inline attachment + // as inline attachments use contentID instead of name + // even when there is a name. + assert.False(suite.T(), ptr.Val(attachments[1].GetIsInline())) + }, + }, + { + name: "multiple attachments without name", + transform: func(msg models.Messageable) { + attachments := msg.GetAttachments() + attachments[1].SetName(ptr.To("")) + attachments[2].SetName(ptr.To("")) + + // This test has to be run on a non inline attachment + // as inline attachments use contentID instead of name + // even when there is a name. + assert.False(suite.T(), ptr.Val(attachments[1].GetIsInline())) + assert.False(suite.T(), ptr.Val(attachments[2].GetIsInline())) + }, + }, } for _, test := range tests { diff --git a/src/internal/converters/eml/testdata/email-with-attachments.json b/src/internal/converters/eml/testdata/email-with-attachments.json index e7e3eb1d6..1b3f1fac5 100644 --- a/src/internal/converters/eml/testdata/email-with-attachments.json +++ b/src/internal/converters/eml/testdata/email-with-attachments.json @@ -104,6 +104,19 @@ "contentId": null, "contentLocation": null, "contentBytes": "W1BhdGhzXQpQcmVmaXggPSAuLgo=" + }, + { + "@odata.type": "#microsoft.graph.fileAttachment", + "@odata.mediaContentType": "application/octet-stream", + "id": "ZZMkAGJiZmE2NGU4LTQ4YjktNDI1Mi1iMWQzLTQ1MmMxODJkZmQyNABGAAAAAABFdiK7oifWRb4ADuqgSRcnBwBBFDg0JJk7TY1fmsJrh7tNAAAAAAEJAABBFDg0JJk7TY1fmsJrh7tNAAEwbDEWAAABEgAQAD3rU0iyzCdHgz0xmOrWc9g=", + "lastModifiedDateTime": "2023-11-16T05:42:47Z", + "name": "qt2.conf", + "contentType": "application/octet-stream", + "size": 156, + "isInline": false, + "contentId": null, + "contentLocation": null, + "contentBytes": "Z1BhdGhzXQpQcmVmaXggPSAuLgo=" } ] }