Compare commits

..

No commits in common. "3154649420179555644057f3c1927f65b88bd917" and "894936092edc287fd9d7f4ce803360894ce8aab2" have entirely different histories.

5 changed files with 95 additions and 50 deletions

View file

@ -19,6 +19,9 @@ type MIMEVersion string
// MIMEType represents the MIME type for the mail // MIMEType represents the MIME type for the mail
type MIMEType string type MIMEType string
// Disposition represents a content disposition for the Msg
type Disposition string
// List of supported encodings // List of supported encodings
const ( const (
// EncodingB64 represents the Base64 encoding as specified in RFC 2045. // EncodingB64 represents the Base64 encoding as specified in RFC 2045.
@ -160,6 +163,11 @@ const (
MIMESMime MIMEType = `signed; protocol="application/pkcs7-signature"; micalg=sha256` MIMESMime MIMEType = `signed; protocol="application/pkcs7-signature"; micalg=sha256`
) )
// List of common content disposition
const (
DispositionSMime Disposition = `attachment; filename="smime.p7s"`
)
// String is a standard method to convert an Charset into a printable format // String is a standard method to convert an Charset into a printable format
func (c Charset) String() string { func (c Charset) String() string {
return string(c) return string(c)
@ -174,3 +182,8 @@ func (c ContentType) String() string {
func (e Encoding) String() string { func (e Encoding) String() string {
return string(e) return string(e)
} }
// String is a standard method to convert an Disposition into a printable format
func (d Disposition) String() string {
return string(d)
}

View file

@ -126,3 +126,22 @@ func TestCharset_String(t *testing.T) {
}) })
} }
} }
// TestDisposition_String tests the string method of the Disposition object
func TestDisposition_String(t *testing.T) {
tests := []struct {
name string
d Disposition
want string
}{
{"Disposition: S/Mime", DispositionSMime, `attachment; filename="smime.p7s"`},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.d.String() != tt.want {
t.Errorf("wrong string for Disposition returned. Expected: %s, got: %s",
tt.want, tt.d.String())
}
})
}
}

View file

@ -269,6 +269,9 @@ func (mw *msgWriter) writePart(part *Part, charset Charset) {
if part.description != "" { if part.description != "" {
mimeHeader.Add(string(HeaderContentDescription), part.description) mimeHeader.Add(string(HeaderContentDescription), part.description)
} }
if part.disposition != "" {
mimeHeader.Add(string(HeaderContentDisposition), part.disposition.String())
}
mimeHeader.Add(string(HeaderContentType), contentType) mimeHeader.Add(string(HeaderContentType), contentType)
mimeHeader.Add(string(HeaderContentTransferEnc), contentTransferEnc) mimeHeader.Add(string(HeaderContentTransferEnc), contentTransferEnc)
mw.newPart(mimeHeader) mw.newPart(mimeHeader)

20
part.go
View file

@ -17,10 +17,10 @@ type Part struct {
contentType ContentType contentType ContentType
charset Charset charset Charset
description string description string
disposition Disposition
encoding Encoding encoding Encoding
isDeleted bool isDeleted bool
writeFunc func(io.Writer) (int64, error) writeFunc func(io.Writer) (int64, error)
smime bool
} }
// GetContent executes the WriteFunc of the Part and returns the content as byte slice // GetContent executes the WriteFunc of the Part and returns the content as byte slice
@ -57,9 +57,9 @@ func (p *Part) GetDescription() string {
return p.description return p.description
} }
// IsSMimeSigned returns true if the Part should be singed with S/MIME // GetDisposition returns the currently set Content-Disposition of the Part
func (p *Part) IsSMimeSigned() bool { func (p *Part) GetDisposition() Disposition {
return p.smime return p.disposition
} }
// SetContent overrides the content of the Part with the given string // SetContent overrides the content of the Part with the given string
@ -88,9 +88,9 @@ func (p *Part) SetDescription(description string) {
p.description = description p.description = description
} }
// SetIsSMimeSigned sets the flag for signing the Part with S/MIME // SetDisposition overrides the Content-Disposition of the Part
func (p *Part) SetIsSMimeSigned(smime bool) { func (p *Part) SetDisposition(disposition Disposition) {
p.smime = smime p.disposition = disposition
} }
// SetWriteFunc overrides the WriteFunc of the Part // SetWriteFunc overrides the WriteFunc of the Part
@ -125,9 +125,9 @@ func WithPartContentDescription(description string) PartOption {
} }
} }
// WithSMimeSinging overrides the flag for signing the Part with S/MIME // WithContentDisposition overrides the default Part Content-Disposition
func WithSMimeSinging() PartOption { func WithContentDisposition(disposition Disposition) PartOption {
return func(p *Part) { return func(p *Part) {
p.smime = true p.disposition = disposition
} }
} }

View file

@ -102,21 +102,32 @@ func TestPart_WithPartContentDescription(t *testing.T) {
} }
} }
// TestPart_WithSMimeSinging tests the WithSMimeSinging method // TestPart_withContentDisposition tests the WithContentDisposition method
func TestPart_WithSMimeSinging(t *testing.T) { func TestPart_withContentDisposition(t *testing.T) {
m := NewMsg() tests := []struct {
part := m.newPart(TypeTextPlain, WithSMimeSinging()) name string
if part == nil { disposition Disposition
t.Errorf("newPart() WithSMimeSinging() failed: no part returned") }{
return {"Part disposition: test", "test"},
{"Part disposition: empty", ""},
} }
if part.smime != true { for _, tt := range tests {
t.Errorf("newPart() WithSMimeSinging() failed: expected: %v, got: %v", true, part.smime) m := NewMsg()
} t.Run(tt.name, func(t *testing.T) {
part.smime = true part := m.newPart(TypeTextPlain, WithContentDisposition(tt.disposition), nil)
part.SetIsSMimeSigned(false) if part == nil {
if part.smime != false { t.Errorf("newPart() WithPartContentDescription() failed: no part returned")
t.Errorf("newPart() SetIsSMimeSigned() failed: expected: %v, got: %v", false, part.smime) return
}
if part.disposition != tt.disposition {
t.Errorf("newPart() WithContentDisposition() failed: expected: %s, got: %s", tt.disposition, part.description)
}
part.disposition = ""
part.SetDisposition(tt.disposition)
if part.disposition != tt.disposition {
t.Errorf("newPart() SetDisposition() failed: expected: %s, got: %s", tt.disposition, part.description)
}
})
} }
} }
@ -263,32 +274,6 @@ func TestPart_GetContentBroken(t *testing.T) {
} }
} }
// TestPart_IsSMimeSigned tests Part.IsSMimeSigned
func TestPart_IsSMimeSigned(t *testing.T) {
tests := []struct {
name string
want bool
}{
{"smime:", true},
{"smime:", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
m := NewMsg()
pl, err := getPartList(m)
if err != nil {
t.Errorf("failed: %s", err)
return
}
pl[0].SetIsSMimeSigned(tt.want)
smime := pl[0].IsSMimeSigned()
if smime != tt.want {
t.Errorf("SetContentType failed. Got: %v, expected: %v", smime, tt.want)
}
})
}
}
// TestPart_SetWriteFunc tests Part.SetWriteFunc // TestPart_SetWriteFunc tests Part.SetWriteFunc
func TestPart_SetWriteFunc(t *testing.T) { func TestPart_SetWriteFunc(t *testing.T) {
c := "This is a test with ümläutß" c := "This is a test with ümläutß"
@ -367,6 +352,31 @@ func TestPart_SetDescription(t *testing.T) {
} }
} }
// TestPart_SetDisposition tests Part.SetDisposition
func TestPart_SetDisposition(t *testing.T) {
c := "This is a test"
d := Disposition("test-disposition")
m := NewMsg()
m.SetBodyString(TypeTextPlain, c)
pl, err := getPartList(m)
if err != nil {
t.Errorf("failed: %s", err)
return
}
pd := pl[0].GetDisposition()
if pd != "" {
t.Errorf("Part.GetDisposition failed. Expected empty description but got: %s", pd)
}
pl[0].SetDisposition(d)
if pl[0].disposition != d {
t.Errorf("Part.SetDisposition failed. Expected description to be: %s, got: %s", d, pd)
}
pd = pl[0].GetDisposition()
if pd != d {
t.Errorf("Part.GetDisposition failed. Expected: %s, got: %s", d, pd)
}
}
// TestPart_Delete tests Part.Delete // TestPart_Delete tests Part.Delete
func TestPart_Delete(t *testing.T) { func TestPart_Delete(t *testing.T) {
c := "This is a test with ümläutß" c := "This is a test with ümläutß"