Compare commits

..

1 commit

Author SHA1 Message Date
Michael Fuchs
c01e7964c3
Merge 894936092e into 580d0b0e4e 2024-09-21 16:53:36 +02:00
8 changed files with 100 additions and 55 deletions

View file

@ -54,7 +54,7 @@ jobs:
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@461ef6c76dfe95d5c364de2f431ddbd31a417628 # v3.26.9 uses: github/codeql-action/init@294a9d92911152fe08befb9ec03e240add280cb3 # v3.26.8
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file. # If you wish to specify custom queries, you can do so here or in a config file.
@ -65,7 +65,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below) # If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@461ef6c76dfe95d5c364de2f431ddbd31a417628 # v3.26.9 uses: github/codeql-action/autobuild@294a9d92911152fe08befb9ec03e240add280cb3 # v3.26.8
# Command-line programs to run using the OS shell. # Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl # 📚 https://git.io/JvXDl
@ -79,4 +79,4 @@ jobs:
# make release # make release
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@461ef6c76dfe95d5c364de2f431ddbd31a417628 # v3.26.9 uses: github/codeql-action/analyze@294a9d92911152fe08befb9ec03e240add280cb3 # v3.26.8

View file

@ -75,6 +75,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard. # Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning" - name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@461ef6c76dfe95d5c364de2f431ddbd31a417628 # v3.26.9 uses: github/codeql-action/upload-sarif@294a9d92911152fe08befb9ec03e240add280cb3 # v3.26.8
with: with:
sarif_file: results.sarif sarif_file: results.sarif

View file

@ -44,7 +44,7 @@ jobs:
run: | run: |
go test -v -race --coverprofile=./cov.out ./... go test -v -race --coverprofile=./cov.out ./...
- uses: sonarsource/sonarqube-scan-action@884b79409bbd464b2a59edc326a4b77dc56b2195 # master - uses: sonarsource/sonarqube-scan-action@0c0f3958d90fc466625f1d1af1f47bddd4cc6bd1 # master
env: env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}

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) {
tests := []struct {
name string
disposition Disposition
}{
{"Part disposition: test", "test"},
{"Part disposition: empty", ""},
}
for _, tt := range tests {
m := NewMsg() m := NewMsg()
part := m.newPart(TypeTextPlain, WithSMimeSinging()) t.Run(tt.name, func(t *testing.T) {
part := m.newPart(TypeTextPlain, WithContentDisposition(tt.disposition), nil)
if part == nil { if part == nil {
t.Errorf("newPart() WithSMimeSinging() failed: no part returned") t.Errorf("newPart() WithPartContentDescription() failed: no part returned")
return return
} }
if part.smime != true { if part.disposition != tt.disposition {
t.Errorf("newPart() WithSMimeSinging() failed: expected: %v, got: %v", true, part.smime) t.Errorf("newPart() WithContentDisposition() failed: expected: %s, got: %s", tt.disposition, part.description)
} }
part.smime = true part.disposition = ""
part.SetIsSMimeSigned(false) part.SetDisposition(tt.disposition)
if part.smime != false { if part.disposition != tt.disposition {
t.Errorf("newPart() SetIsSMimeSigned() failed: expected: %v, got: %v", false, part.smime) 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ß"