fix(GODT-3087): Exclude attachment content-disposition part when determining...
This commit is contained in:
parent
96773f3225
commit
1c385d5c9b
|
@ -147,7 +147,7 @@ func parse(p *parser.Parser, allowInvalidAddressLists bool) (Message, error) {
|
|||
m.PlainBody = Body(plainBody)
|
||||
m.MIMEBody = MIMEBody(mimeBody)
|
||||
|
||||
mimeType, err := determineMIMEType(p)
|
||||
mimeType, err := determineBodyMIMEType(p)
|
||||
if err != nil {
|
||||
return Message{}, errors.Wrap(err, "failed to get mime type")
|
||||
}
|
||||
|
@ -313,7 +313,7 @@ func collectBodyParts(p *parser.Parser, preferredContentType string) (parser.Par
|
|||
return bestChoice(childParts, preferredContentType), nil
|
||||
}).
|
||||
RegisterRule("text/plain", func(p *parser.Part, visit parser.Visit) (interface{}, error) {
|
||||
disp, _, err := p.Header.ContentDisposition()
|
||||
disp, _, err := p.ContentDisposition()
|
||||
if err != nil {
|
||||
disp = ""
|
||||
}
|
||||
|
@ -325,7 +325,7 @@ func collectBodyParts(p *parser.Parser, preferredContentType string) (parser.Par
|
|||
return parser.Parts{p}, nil
|
||||
}).
|
||||
RegisterRule("text/html", func(p *parser.Part, visit parser.Visit) (interface{}, error) {
|
||||
disp, _, err := p.Header.ContentDisposition()
|
||||
disp, _, err := p.ContentDisposition()
|
||||
if err != nil {
|
||||
disp = ""
|
||||
}
|
||||
|
@ -405,7 +405,7 @@ func allPartsHaveContentType(parts parser.Parts, contentType string) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func determineMIMEType(p *parser.Parser) (string, error) {
|
||||
func determineBodyMIMEType(p *parser.Parser) (string, error) {
|
||||
var isHTML bool
|
||||
|
||||
w := p.NewWalker().
|
||||
|
@ -414,7 +414,7 @@ func determineMIMEType(p *parser.Parser) (string, error) {
|
|||
return
|
||||
})
|
||||
|
||||
if err := w.Walk(); err != nil {
|
||||
if err := w.WalkSkipAttachment(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,10 @@ func (h *handler) matchPart(p *Part) bool {
|
|||
return h.matchType(p) || h.matchDisp(p)
|
||||
}
|
||||
|
||||
func (h *handler) matchPartSkipAttachment(p *Part) bool {
|
||||
return !p.isAttachment() && h.matchPart(p)
|
||||
}
|
||||
|
||||
func (h *handler) matchType(p *Part) bool {
|
||||
if h.typeRegExp == nil {
|
||||
return false
|
||||
|
@ -50,7 +54,7 @@ func (h *handler) matchDisp(p *Part) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
disp, _, err := p.Header.ContentDisposition()
|
||||
disp, _, err := p.ContentDisposition()
|
||||
if err != nil {
|
||||
disp = ""
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ func (p *Part) ContentType() (string, map[string]string, error) {
|
|||
}
|
||||
|
||||
func (p *Part) ContentDisposition() (string, map[string]string, error) {
|
||||
return p.Header.ContentDisposition()
|
||||
return pmmime.ParseMediaType(p.Header.Get("Content-Disposition"))
|
||||
}
|
||||
|
||||
func (p *Part) HasContentID() bool {
|
||||
|
@ -209,6 +209,14 @@ func (p *Part) isMultipartMixedOrRelated() bool {
|
|||
return t == "multipart/mixed" || t == "multipart/related"
|
||||
}
|
||||
|
||||
func (p *Part) isAttachment() bool {
|
||||
disp, _, err := p.ContentDisposition()
|
||||
if err != nil {
|
||||
disp = ""
|
||||
}
|
||||
return disp == "attachment"
|
||||
}
|
||||
|
||||
func getContentHeaders(header message.Header) message.Header {
|
||||
var res message.Header
|
||||
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
Content-Type: multipart/mixed; boundary="------------MQ01Z9UM8OaR9z39TvzDfdIq"
|
||||
Subject: Fwd: Reply to this message, it has various attachments.
|
||||
References: <something@protonmail.ch>
|
||||
To: <[user:user2]@[domain]>
|
||||
From: <[user:user]@[domain]>
|
||||
In-Reply-To: <something@protonmail.ch>
|
||||
X-Forwarded-Message-Id: <something@protonmail.ch>
|
||||
|
||||
This is a multi-part message in MIME format.
|
||||
--------------MQ01Z9UM8OaR9z39TvzDfdIq
|
||||
Content-Type: text/plain; charset=UTF-8; format=flowed
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Forwarding a message with various attachments in it!
|
||||
|
||||
|
||||
|
||||
-------- Forwarded Message --------
|
||||
Subject: Reply to this message, it has various attachments.
|
||||
Date: Thu, 26 Oct 2023 10:41:55 +0000
|
||||
From: Gjorgji Testing <gorgitesting@protonmail.com>
|
||||
Reply-To: Gjorgji Testing <gorgitesting@protonmail.com>
|
||||
To: Gjorgji Test v3 <gorgitesting3@protonmail.com>
|
||||
|
||||
|
||||
|
||||
|
||||
For real!
|
||||
|
||||
*Gjorgji Testing
|
||||
TesASID <https://www.youtube.com/watch?v=MifXUbrjYr8>
|
||||
*
|
||||
--------------MQ01Z9UM8OaR9z39TvzDfdIq
|
||||
Content-Type: text/html; charset=UTF-8; name="index.html"
|
||||
Content-Disposition: attachment; filename="index.html"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
IDwhRE9DVFlQRSBodG1sPg0KPGh0bWw+DQo8aGVhZD4NCjx0aXRsZT5QYWdlIFRpdGxlPC90
|
||||
aXRsZT4NCjwvaGVhZD4NCjxib2R5Pg0KDQo8aDE+TXkgRmlyc3QgSGVhZGluZzwvaDE+DQo8
|
||||
cD5NeSBmaXJzdCBwYXJhZ3JhcGguPC9wPg0KDQo8L2JvZHk+DQo8L2h0bWw+IA==
|
||||
--------------MQ01Z9UM8OaR9z39TvzDfdIq
|
||||
Content-Type: text/xml; charset=UTF-8; name="testxml.xml"
|
||||
Content-Disposition: attachment; filename="testxml.xml"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHN1aXRl
|
||||
IFNZU1RFTSAiaHR0cDovL3Rlc3RuZy5vcmcvdGVzdG5nLTEuMC5kdGQiID4KCjxzdWl0ZSBu
|
||||
YW1lPSJBZmZpbGlhdGUgTmV0d29ya3MiPgoKICAgIDx0ZXN0IG5hbWU9IkFmZmlsaWF0ZSBO
|
||||
ZXR3b3JrcyIgZW5hYmxlZD0idHJ1ZSI+CiAgICAgICAgPGNsYXNzZXM+CiAgICAgICAgICAg
|
||||
IDxjbGFzcyBuYW1lPSJjb20uY2xpY2tvdXQuYXBpdGVzdGluZy5hZmZOZXR3b3Jrcy5Bd2lu
|
||||
VUtUZXN0Ii8+CiAgICAgICAgPC9jbGFzc2VzPgogICAgPC90ZXN0PgoKPC9zdWl0ZT4=
|
||||
--------------MQ01Z9UM8OaR9z39TvzDfdIq
|
||||
Content-Type: application/pdf; name="test.pdf"
|
||||
Content-Disposition: attachment; filename="test.pdf"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
JVBERi0xLjUKJeLjz9MKNyAwIG9iago8PAovVHlwZSAvRm9udERlc2NyaXB0b3IKL0ZvbnRO
|
||||
MjM0NAolJUVPRgo=
|
||||
--------------MQ01Z9UM8OaR9z39TvzDfdIq
|
||||
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;
|
||||
name="test.xlsx"
|
||||
Content-Disposition: attachment; filename="test.xlsx"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
UEsDBBQABgAIAAAAIQBi7p1oXgEAAJAEAAATAAgCW0NvbnRlbnRfVHlwZXNdLnhtbCCiBAIo
|
||||
UQIAABEAAAAAAAAAAAAAAAAARBcAAGRvY1Byb3BzL2NvcmUueG1sUEsBAi0AFAAGAAgAAAAh
|
||||
AGFJCRCJAQAAEQMAABAAAAAAAAAAAAAAAAAAvBkAAGRvY1Byb3BzL2FwcC54bWxQSwUGAAAA
|
||||
AAoACgCAAgAAexwAAAAA
|
||||
--------------MQ01Z9UM8OaR9z39TvzDfdIq
|
||||
Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document;
|
||||
name="test.docx"
|
||||
Content-Disposition: attachment; filename="test.docx"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
UEsDBBQABgAIAAAAIQDfpNJsWgEAACAFAAATAAgCW0NvbnRlbnRfVHlwZXNdLnhtbCCiBAIo
|
||||
cHAueG1sUEsBAi0AFAAGAAgAAAAhABA0tG9uAQAA4QIAABEAAAAAAAAAAAAAAAAA2xsAAGRv
|
||||
Y1Byb3BzL2NvcmUueG1sUEsBAi0AFAAGAAgAAAAhAJ/mlBIqCwAAU3AAAA8AAAAAAAAAAAAA
|
||||
AAAAgB4AAHdvcmQvc3R5bGVzLnhtbFBLBQYAAAAACwALAMECAADXKQAAAAA=
|
||||
--------------MQ01Z9UM8OaR9z39TvzDfdIq
|
||||
Content-Type: text/plain; charset=UTF-8; name="text file.txt"
|
||||
Content-Disposition: attachment; filename="text file.txt"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
dGV4dCBmaWxl
|
||||
|
||||
--------------MQ01Z9UM8OaR9z39TvzDfdIq--
|
|
@ -33,9 +33,12 @@ func newWalker(root *Part) *Walker {
|
|||
}
|
||||
}
|
||||
|
||||
func (w *Walker) Walk() (err error) {
|
||||
func (w *Walker) Walk() error {
|
||||
return w.walkOverPart(w.root)
|
||||
}
|
||||
func (w *Walker) WalkSkipAttachment() error {
|
||||
return w.walkOverPartSkipAttachment(w.root)
|
||||
}
|
||||
|
||||
func (w *Walker) walkOverPart(p *Part) error {
|
||||
if err := w.getHandlerFunc(p)(p); err != nil {
|
||||
|
@ -51,6 +54,20 @@ func (w *Walker) walkOverPart(p *Part) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (w *Walker) walkOverPartSkipAttachment(p *Part) error {
|
||||
if err := w.getHandlerFuncSkipAttachment(p)(p); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, child := range p.children {
|
||||
if err := w.walkOverPartSkipAttachment(child); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterDefaultHandler registers a handler that will be called on every part
|
||||
// that doesn't match a registered content type/disposition handler.
|
||||
func (w *Walker) RegisterDefaultHandler(fn HandlerFunc) *Walker {
|
||||
|
@ -91,3 +108,13 @@ func (w *Walker) getHandlerFunc(p *Part) HandlerFunc {
|
|||
|
||||
return w.defaultHandler
|
||||
}
|
||||
|
||||
func (w *Walker) getHandlerFuncSkipAttachment(p *Part) HandlerFunc {
|
||||
for _, handler := range w.handlers {
|
||||
if handler.matchPartSkipAttachment(p) {
|
||||
return handler.fn
|
||||
}
|
||||
}
|
||||
|
||||
return w.defaultHandler
|
||||
}
|
||||
|
|
|
@ -60,6 +60,27 @@ func TestWalkerTypeHandler(t *testing.T) {
|
|||
}, html)
|
||||
}
|
||||
|
||||
func TestWalkerTypeHandler_excludingAttachment(t *testing.T) {
|
||||
p := newTestParser(t, "forwarding_html_attachment.eml")
|
||||
|
||||
html := [][]byte{}
|
||||
plain := [][]byte{}
|
||||
|
||||
walker := p.NewWalker().
|
||||
RegisterContentTypeHandler("text/html", func(p *Part) (err error) {
|
||||
html = append(html, p.Body)
|
||||
return
|
||||
}).
|
||||
RegisterContentTypeHandler("text/plain", func(p *Part) (err error) {
|
||||
plain = append(plain, p.Body)
|
||||
return
|
||||
})
|
||||
|
||||
assert.NoError(t, walker.WalkSkipAttachment())
|
||||
assert.Equal(t, 1, len(plain))
|
||||
assert.Equal(t, 0, len(html))
|
||||
}
|
||||
|
||||
func TestWalkerDispositionHandler(t *testing.T) {
|
||||
p := newTestParser(t, "text_html_octet_attachment.eml")
|
||||
|
||||
|
|
|
@ -480,3 +480,170 @@ Feature: SMTP sending of plain messages
|
|||
}
|
||||
}
|
||||
"""
|
||||
|
||||
Scenario: Forward a message containing various attachments
|
||||
When SMTP client "1" sends the following message from "[user:user]@[domain]" to "[user:user2]@[domain]":
|
||||
"""
|
||||
Content-Type: multipart/mixed; boundary="------------MQ01Z9UM8OaR9z39TvzDfdIq"
|
||||
Subject: Fwd: Reply to this message, it has various attachments.
|
||||
References: <something@protonmail.ch>
|
||||
To: <[user:user2]@[domain]>
|
||||
From: <[user:user]@[domain]>
|
||||
In-Reply-To: <something@protonmail.ch>
|
||||
X-Forwarded-Message-Id: <something@protonmail.ch>
|
||||
|
||||
This is a multi-part message in MIME format.
|
||||
--------------MQ01Z9UM8OaR9z39TvzDfdIq
|
||||
Content-Type: text/plain; charset=UTF-8; format=flowed
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Forwarding a message with various attachments in it!
|
||||
|
||||
|
||||
|
||||
-------- Forwarded Message --------
|
||||
Subject: Reply to this message, it has various attachments.
|
||||
Date: Thu, 26 Oct 2023 10:41:55 +0000
|
||||
From: Gjorgji Testing <gorgitesting@protonmail.com>
|
||||
Reply-To: Gjorgji Testing <gorgitesting@protonmail.com>
|
||||
To: Gjorgji Test v3 <gorgitesting3@protonmail.com>
|
||||
|
||||
|
||||
|
||||
|
||||
For real!
|
||||
|
||||
*Gjorgji Testing
|
||||
TesASID <https://www.youtube.com/watch?v=MifXUbrjYr8>
|
||||
*
|
||||
--------------MQ01Z9UM8OaR9z39TvzDfdIq
|
||||
Content-Type: text/html; charset=UTF-8; name="index.html"
|
||||
Content-Disposition: attachment; filename="index.html"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
IDwhRE9DVFlQRSBodG1sPg0KPGh0bWw+DQo8aGVhZD4NCjx0aXRsZT5QYWdlIFRpdGxlPC90
|
||||
aXRsZT4NCjwvaGVhZD4NCjxib2R5Pg0KDQo8aDE+TXkgRmlyc3QgSGVhZGluZzwvaDE+DQo8
|
||||
cD5NeSBmaXJzdCBwYXJhZ3JhcGguPC9wPg0KDQo8L2JvZHk+DQo8L2h0bWw+IA==
|
||||
--------------MQ01Z9UM8OaR9z39TvzDfdIq
|
||||
Content-Type: text/xml; charset=UTF-8; name="testxml.xml"
|
||||
Content-Disposition: attachment; filename="testxml.xml"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHN1aXRl
|
||||
IFNZU1RFTSAiaHR0cDovL3Rlc3RuZy5vcmcvdGVzdG5nLTEuMC5kdGQiID4KCjxzdWl0ZSBu
|
||||
YW1lPSJBZmZpbGlhdGUgTmV0d29ya3MiPgoKICAgIDx0ZXN0IG5hbWU9IkFmZmlsaWF0ZSBO
|
||||
ZXR3b3JrcyIgZW5hYmxlZD0idHJ1ZSI+CiAgICAgICAgPGNsYXNzZXM+CiAgICAgICAgICAg
|
||||
IDxjbGFzcyBuYW1lPSJjb20uY2xpY2tvdXQuYXBpdGVzdGluZy5hZmZOZXR3b3Jrcy5Bd2lu
|
||||
VUtUZXN0Ii8+CiAgICAgICAgPC9jbGFzc2VzPgogICAgPC90ZXN0PgoKPC9zdWl0ZT4=
|
||||
--------------MQ01Z9UM8OaR9z39TvzDfdIq
|
||||
Content-Type: application/pdf; name="test.pdf"
|
||||
Content-Disposition: attachment; filename="test.pdf"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
JVBERi0xLjUKJeLjz9MKNyAwIG9iago8PAovVHlwZSAvRm9udERlc2NyaXB0b3IKL0ZvbnRO
|
||||
MjM0NAolJUVPRgo=
|
||||
--------------MQ01Z9UM8OaR9z39TvzDfdIq
|
||||
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;
|
||||
name="test.xlsx"
|
||||
Content-Disposition: attachment; filename="test.xlsx"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
UEsDBBQABgAIAAAAIQBi7p1oXgEAAJAEAAATAAgCW0NvbnRlbnRfVHlwZXNdLnhtbCCiBAIo
|
||||
UQIAABEAAAAAAAAAAAAAAAAARBcAAGRvY1Byb3BzL2NvcmUueG1sUEsBAi0AFAAGAAgAAAAh
|
||||
AGFJCRCJAQAAEQMAABAAAAAAAAAAAAAAAAAAvBkAAGRvY1Byb3BzL2FwcC54bWxQSwUGAAAA
|
||||
AAoACgCAAgAAexwAAAAA
|
||||
--------------MQ01Z9UM8OaR9z39TvzDfdIq
|
||||
Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document;
|
||||
name="test.docx"
|
||||
Content-Disposition: attachment; filename="test.docx"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
UEsDBBQABgAIAAAAIQDfpNJsWgEAACAFAAATAAgCW0NvbnRlbnRfVHlwZXNdLnhtbCCiBAIo
|
||||
cHAueG1sUEsBAi0AFAAGAAgAAAAhABA0tG9uAQAA4QIAABEAAAAAAAAAAAAAAAAA2xsAAGRv
|
||||
Y1Byb3BzL2NvcmUueG1sUEsBAi0AFAAGAAgAAAAhAJ/mlBIqCwAAU3AAAA8AAAAAAAAAAAAA
|
||||
AAAAgB4AAHdvcmQvc3R5bGVzLnhtbFBLBQYAAAAACwALAMECAADXKQAAAAA=
|
||||
--------------MQ01Z9UM8OaR9z39TvzDfdIq
|
||||
Content-Type: text/plain; charset=UTF-8; name="text file.txt"
|
||||
Content-Disposition: attachment; filename="text file.txt"
|
||||
Content-Transfer-Encoding: base64
|
||||
|
||||
dGV4dCBmaWxl
|
||||
|
||||
--------------MQ01Z9UM8OaR9z39TvzDfdIq--
|
||||
|
||||
"""
|
||||
Then it succeeds
|
||||
When user "[user:user]" connects and authenticates IMAP client "1"
|
||||
Then IMAP client "1" eventually sees the following messages in "Sent":
|
||||
| from | to | subject | X-Forwarded-Message-Id |
|
||||
| [user:user]@[domain] | [user:user2]@[domain] | Fwd: Reply to this message, it has various attachments. | something@protonmail.ch |
|
||||
And IMAP client "1" eventually sees 1 messages in "Sent"
|
||||
When the user logs in with username "[user:user2]" and password "password"
|
||||
And user "[user:user2]" connects and authenticates IMAP client "2"
|
||||
And user "[user:user2]" finishes syncing
|
||||
And it succeeds
|
||||
Then IMAP client "2" eventually sees the following messages in "Inbox":
|
||||
| from | to | subject | X-Forwarded-Message-Id |
|
||||
| [user:user]@[domain] | [user:user2]@[domain] | Fwd: Reply to this message, it has various attachments. | something@protonmail.ch |
|
||||
Then IMAP client "2" eventually sees the following message in "Inbox" with this structure:
|
||||
"""
|
||||
{
|
||||
"from": "[user:user]@[domain]",
|
||||
"to": "[user:user2]@[domain]",
|
||||
"subject": "Fwd: Reply to this message, it has various attachments.",
|
||||
"content": {
|
||||
"content-type": "multipart/mixed",
|
||||
"sections":[
|
||||
{
|
||||
"content-type": "text/plain",
|
||||
"content-type-charset": "utf-8",
|
||||
"transfer-encoding": "quoted-printable",
|
||||
"body-is": "Forwarding a message with various attachments in it!\r\n\r\n\r\n\r\n-------- Forwarded Message --------\r\nSubject: \tReply to this message, it has various attachments.\r\nDate: \tThu, 26 Oct 2023 10:41:55 +0000\r\nFrom: \tGjorgji Testing <gorgitesting@protonmail.com>\r\nReply-To: \tGjorgji Testing <gorgitesting@protonmail.com>\r\nTo: \tGjorgji Test v3 <gorgitesting3@protonmail.com>\r\n\r\n\r\n\r\n\r\nFor real!\r\n\r\n*Gjorgji Testing\r\nTesASID <https://www.youtube.com/watch?v=3DMifXUbrjYr8>\r\n*"
|
||||
},
|
||||
{
|
||||
"content-type": "text/html",
|
||||
"content-type-name": "index.html",
|
||||
"content-disposition": "attachment",
|
||||
"content-disposition-filename": "index.html",
|
||||
"transfer-encoding": "base64"
|
||||
},
|
||||
{
|
||||
"content-type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
"content-type-name": "test.docx",
|
||||
"content-disposition": "attachment",
|
||||
"content-disposition-filename": "test.docx",
|
||||
"transfer-encoding": "base64"
|
||||
},
|
||||
{
|
||||
"content-type": "application/pdf",
|
||||
"content-type-name": "test.pdf",
|
||||
"content-disposition": "attachment",
|
||||
"content-disposition-filename": "test.pdf",
|
||||
"transfer-encoding": "base64"
|
||||
},
|
||||
{
|
||||
"content-type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
"content-type-name": "test.xlsx",
|
||||
"content-disposition": "attachment",
|
||||
"content-disposition-filename": "test.xlsx",
|
||||
"transfer-encoding": "base64"
|
||||
},
|
||||
{
|
||||
"content-type": "text/xml",
|
||||
"content-type-name": "testxml.xml",
|
||||
"content-disposition": "attachment",
|
||||
"content-disposition-filename": "testxml.xml",
|
||||
"transfer-encoding": "base64"
|
||||
},
|
||||
{
|
||||
"content-type": "text/plain",
|
||||
"content-type-name": "text file.txt",
|
||||
"content-disposition": "attachment",
|
||||
"content-disposition-filename": "text file.txt",
|
||||
"transfer-encoding": "base64",
|
||||
"body-is": "dGV4dCBmaWxl"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
"""
|
Loading…
Reference in New Issue