From 3e7d4f817554fe35f0215b6d36e25a47641ca52b Mon Sep 17 00:00:00 2001 From: Ralph Slooten Date: Thu, 5 Dec 2024 19:41:34 +1300 Subject: [PATCH] Feature: Add support for sending inline attachments via HTTP API (#399) Optional settings for Attachment ContentID & ContentType --- server/apiv1/send.go | 30 +++++++++++++++++++++++------- server/server_test.go | 10 ++++++++++ server/ui/api/v1/swagger.json | 18 ++++++++++++++---- 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/server/apiv1/send.go b/server/apiv1/send.go index 55ec8746bf..7f006ccfde 100644 --- a/server/apiv1/send.go +++ b/server/apiv1/send.go @@ -80,23 +80,33 @@ type SendRequest struct { Subject string // Message body (text) - // example: This is the text body + // example: Mailpit is awesome! Text string // Message body (HTML) - // example:

Mailpit is awesome!

+ // example:

Mailpit is awesome!

HTML string // Attachments Attachments []struct { // Base64-encoded string of the file content // required: true - // example: VGhpcyBpcyBhIHBsYWluIHRleHQgYXR0YWNobWVudA== + // example: iVBORw0KGgoAAAANSUhEUgAAAEEAAAA8CAMAAAAOlSdoAAAACXBIWXMAAAHrAAAB6wGM2bZBAAAAS1BMVEVHcEwRfnUkZ2gAt4UsSF8At4UtSV4At4YsSV4At4YsSV8At4YsSV4At4YsSV4sSV4At4YsSV4At4YtSV4At4YsSV4At4YtSV8At4YsUWYNAAAAGHRSTlMAAwoXGiktRE5dbnd7kpOlr7zJ0d3h8PD8PCSRAAACWUlEQVR42pXT4ZaqIBSG4W9rhqQYocG+/ys9Y0Z0Br+x3j8zaxUPewFh65K+7yrIMeIY4MT3wPfEJCidKXEMnLaVkxDiELiMz4WEOAZSFghxBIypCOlKiAMgXfIqTnBgSm8CIQ6BImxEUxEckClVQiHGj4Ba4AQHikAIClwTE9KtIghAhUJwoLkmLnCiAHJLRKgIMsEtVUKbBUIwoAg2C4QgQBE6l4VCnApBgSKYLLApCnCa0+96AEMW2BQcmC+Pr3nfp7o5Exy49gIADcIqUELGfeA+bp93LmAJp8QJoEcN3C7NY3sbVANixMyI0nku20/n5/ZRf3KI2k6JEDWQtxcbdGuAqu3TAXG+/799Oyyas1B1MnMiA+XyxHp9q0PUKGPiRAau1fZbLRZV09wZcT8/gHk8QQAxXn8VgaDqcUmU6O/r28nbVwXAqca2mRNtPAF5+zoP2MeN9Fy4NgC6RfcbgE7XITBRYTtOE3U3C2DVff7pk+PkUxgAbvtnPXJaD6DxulMLwOhPS/M3MQkgg1ZFrIXnmfaZoOfpKiFgzeZD/WuKqQEGrfJYkyWf6vlG3xUgTuscnkNkQsb599q124kdpMUjCa/XARHs1gZymVtGt3wLkiFv8rUgTxitYCex5EVGec0Y9VmoDTFBSQte2TfXGXlf7hbdaUM9Sk7fisEN9qfBBTK+FZcvM9fQSdkl2vj4W2oX/bRogO3XasiNH7R0eW7fgRM834ImTg+Lg6BEnx4vz81rhr+MYPBBQg1v8GndEOrthxaCTxNAOut8WKLGZQl+MPz88Q9tAO/hVuSeqQAAAABJRU5ErkJggg== Content string // Filename // required: true - // example: AttachedFile.txt + // example: mailpit.png Filename string + // Optional Content Type for the the attachment. + // If this field is not set (or empty) then the content type is automatically detected. + // required: false + // example: image/png + ContentType string + // Optional Content-ID (`cid`) for attachment. + // If this field is set then the file is attached inline. + // required: false + // example: mailpit-logo + ContentID string } // Mailpit tags @@ -269,9 +279,15 @@ func (d SendRequest) Send(remoteAddr string) (string, error) { if err != nil { return "", fmt.Errorf("error decoding base64 attachment \"%s\": %s", a.Filename, err.Error()) } - - mimeType := http.DetectContentType(b) - msg = msg.AddAttachment(b, mimeType, a.Filename) + contentType := http.DetectContentType(b) + if a.ContentType != "" { + contentType = a.ContentType + } + if a.ContentID != "" { + msg = msg.AddInline(b, contentType, a.Filename, a.ContentID) + } else { + msg = msg.AddAttachment(b, contentType, a.Filename) + } } } diff --git a/server/server_test.go b/server/server_test.go index da9a53e34a..199d96982e 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -243,6 +243,12 @@ func TestAPIv1Send(t *testing.T) { { "Content": "VGhpcyBpcyBhIHBsYWluIHRleHQgYXR0YWNobWVudA==", "Filename": "Attached File.txt" + }, + { + "Content": "iVBORw0KGgoAAAANSUhEUgAAAEEAAAA8CAMAAAAOlSdoAAAACXBIWXMAAAHrAAAB6wGM2bZBAAAAS1BMVEVHcEwRfnUkZ2gAt4UsSF8At4UtSV4At4YsSV4At4YsSV8At4YsSV4At4YsSV4sSV4At4YsSV4At4YtSV4At4YsSV4At4YtSV8At4YsUWYNAAAAGHRSTlMAAwoXGiktRE5dbnd7kpOlr7zJ0d3h8PD8PCSRAAACWUlEQVR42pXT4ZaqIBSG4W9rhqQYocG+/ys9Y0Z0Br+x3j8zaxUPewFh65K+7yrIMeIY4MT3wPfEJCidKXEMnLaVkxDiELiMz4WEOAZSFghxBIypCOlKiAMgXfIqTnBgSm8CIQ6BImxEUxEckClVQiHGj4Ba4AQHikAIClwTE9KtIghAhUJwoLkmLnCiAHJLRKgIMsEtVUKbBUIwoAg2C4QgQBE6l4VCnApBgSKYLLApCnCa0+96AEMW2BQcmC+Pr3nfp7o5Exy49gIADcIqUELGfeA+bp93LmAJp8QJoEcN3C7NY3sbVANixMyI0nku20/n5/ZRf3KI2k6JEDWQtxcbdGuAqu3TAXG+/799Oyyas1B1MnMiA+XyxHp9q0PUKGPiRAau1fZbLRZV09wZcT8/gHk8QQAxXn8VgaDqcUmU6O/r28nbVwXAqca2mRNtPAF5+zoP2MeN9Fy4NgC6RfcbgE7XITBRYTtOE3U3C2DVff7pk+PkUxgAbvtnPXJaD6DxulMLwOhPS/M3MQkgg1ZFrIXnmfaZoOfpKiFgzeZD/WuKqQEGrfJYkyWf6vlG3xUgTuscnkNkQsb599q124kdpMUjCa/XARHs1gZymVtGt3wLkiFv8rUgTxitYCex5EVGec0Y9VmoDTFBSQte2TfXGXlf7hbdaUM9Sk7fisEN9qfBBTK+FZcvM9fQSdkl2vj4W2oX/bRogO3XasiNH7R0eW7fgRM834ImTg+Lg6BEnx4vz81rhr+MYPBBQg1v8GndEOrthxaCTxNAOut8WKLGZQl+MPz88Q9tAO/hVuSeqQAAAABJRU5ErkJggg==", + "Filename": "logo.png", + "ContentID": "inline-cid", + "ContentType": "overridden/type" } ], "ReplyTo": [ @@ -294,6 +300,10 @@ func TestAPIv1Send(t *testing.T) { assertEqual(t, `Tag 1,Tag 2`, strings.Join(msg.Tags, ","), "wrong Tags") assertEqual(t, 1, len(msg.Attachments), "wrong Attachment count") assertEqual(t, `Attached File.txt`, msg.Attachments[0].FileName, "wrong Attachment name") + assertEqual(t, `text/plain`, msg.Attachments[0].ContentType, "wrong Content-Type") + assertEqual(t, 1, len(msg.Inline), "wrong inline Attachment count") + assertEqual(t, `logo.png`, msg.Inline[0].FileName, "wrong Attachment name") + assertEqual(t, `overridden/type`, msg.Inline[0].ContentType, "wrong Content-Type") attachmentBytes, err := clientGet(ts.URL + "/api/v1/message/" + resp.ID + "/part/" + msg.Attachments[0].PartID) if err != nil { diff --git a/server/ui/api/v1/swagger.json b/server/ui/api/v1/swagger.json index d8444c3ab9..d74983ad3b 100644 --- a/server/ui/api/v1/swagger.json +++ b/server/ui/api/v1/swagger.json @@ -1634,12 +1634,22 @@ "Content": { "description": "Base64-encoded string of the file content", "type": "string", - "example": "VGhpcyBpcyBhIHBsYWluIHRleHQgYXR0YWNobWVudA==" + "example": "iVBORw0KGgoAAAANSUhEUgAAAEEAAAA8CAMAAAAOlSdoAAAACXBIWXMAAAHrAAAB6wGM2bZBAAAAS1BMVEVHcEwRfnUkZ2gAt4UsSF8At4UtSV4At4YsSV4At4YsSV8At4YsSV4At4YsSV4sSV4At4YsSV4At4YtSV4At4YsSV4At4YtSV8At4YsUWYNAAAAGHRSTlMAAwoXGiktRE5dbnd7kpOlr7zJ0d3h8PD8PCSRAAACWUlEQVR42pXT4ZaqIBSG4W9rhqQYocG+/ys9Y0Z0Br+x3j8zaxUPewFh65K+7yrIMeIY4MT3wPfEJCidKXEMnLaVkxDiELiMz4WEOAZSFghxBIypCOlKiAMgXfIqTnBgSm8CIQ6BImxEUxEckClVQiHGj4Ba4AQHikAIClwTE9KtIghAhUJwoLkmLnCiAHJLRKgIMsEtVUKbBUIwoAg2C4QgQBE6l4VCnApBgSKYLLApCnCa0+96AEMW2BQcmC+Pr3nfp7o5Exy49gIADcIqUELGfeA+bp93LmAJp8QJoEcN3C7NY3sbVANixMyI0nku20/n5/ZRf3KI2k6JEDWQtxcbdGuAqu3TAXG+/799Oyyas1B1MnMiA+XyxHp9q0PUKGPiRAau1fZbLRZV09wZcT8/gHk8QQAxXn8VgaDqcUmU6O/r28nbVwXAqca2mRNtPAF5+zoP2MeN9Fy4NgC6RfcbgE7XITBRYTtOE3U3C2DVff7pk+PkUxgAbvtnPXJaD6DxulMLwOhPS/M3MQkgg1ZFrIXnmfaZoOfpKiFgzeZD/WuKqQEGrfJYkyWf6vlG3xUgTuscnkNkQsb599q124kdpMUjCa/XARHs1gZymVtGt3wLkiFv8rUgTxitYCex5EVGec0Y9VmoDTFBSQte2TfXGXlf7hbdaUM9Sk7fisEN9qfBBTK+FZcvM9fQSdkl2vj4W2oX/bRogO3XasiNH7R0eW7fgRM834ImTg+Lg6BEnx4vz81rhr+MYPBBQg1v8GndEOrthxaCTxNAOut8WKLGZQl+MPz88Q9tAO/hVuSeqQAAAABJRU5ErkJggg==" + }, + "ContentID": { + "description": "Optional Content-ID (`cid`) for attachment.\nIf this field is set then the file is attached inline.", + "type": "string", + "example": "mailpit-logo" + }, + "ContentType": { + "description": "Optional Content Type for the the attachment.\nIf this field is not set (or empty) then the content type is automatically detected.", + "type": "string", + "example": "image/png" }, "Filename": { "description": "Filename", "type": "string", - "example": "AttachedFile.txt" + "example": "mailpit.png" } } } @@ -1698,7 +1708,7 @@ "HTML": { "description": "Message body (HTML)", "type": "string", - "example": "\u003cp style=\"font-family: arial\"\u003eMailpit is \u003cb\u003eawesome\u003c/b\u003e!\u003c/p\u003e" + "example": "\u003cdiv style=\"text-align:center\"\u003e\u003cp style=\"font-family: arial; font-size: 24px;\"\u003eMailpit is \u003cb\u003eawesome\u003c/b\u003e!\u003c/p\u003e\u003cp\u003e\u003cimg src=\"cid:mailpit-logo\" /\u003e\u003c/p\u003e\u003c/div\u003e" }, "Headers": { "description": "Optional headers in {\"key\":\"value\"} format", @@ -1751,7 +1761,7 @@ "Text": { "description": "Message body (text)", "type": "string", - "example": "This is the text body" + "example": "Mailpit is awesome!" }, "To": { "description": "\"To\" recipients",