mirror of
https://github.com/wneessen/go-mail.git
synced 2024-11-22 05:40:50 +01:00
Compare commits
5 commits
53566a93cd
...
f5bbc558b2
Author | SHA1 | Date | |
---|---|---|---|
f5bbc558b2 | |||
93611e47a5 | |||
f01047855f | |||
3facbde703 | |||
59e85809f7 |
7 changed files with 35 additions and 26 deletions
6
.github/workflows/codecov.yml
vendored
6
.github/workflows/codecov.yml
vendored
|
@ -33,7 +33,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
go: [1.17, 1.18, 1.19, '1.20', '1.21']
|
go: [1.18, 1.19, '1.20', '1.21', '1.22']
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Code
|
- name: Checkout Code
|
||||||
uses: actions/checkout@master
|
uses: actions/checkout@master
|
||||||
|
@ -42,14 +42,14 @@ jobs:
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.go }}
|
go-version: ${{ matrix.go }}
|
||||||
- name: Install sendmail
|
- name: Install sendmail
|
||||||
if: matrix.go == '1.21' && matrix.os == 'ubuntu-latest'
|
if: matrix.go == '1.22' && matrix.os == 'ubuntu-latest'
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get -y install sendmail; which sendmail
|
sudo apt-get -y install sendmail; which sendmail
|
||||||
- name: Run Tests
|
- name: Run Tests
|
||||||
run: |
|
run: |
|
||||||
go test -v -race --coverprofile=coverage.coverprofile --covermode=atomic ./...
|
go test -v -race --coverprofile=coverage.coverprofile --covermode=atomic ./...
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
if: success() && matrix.go == '1.21' && matrix.os == 'ubuntu-latest'
|
if: success() && matrix.go == '1.22' && matrix.os == 'ubuntu-latest'
|
||||||
uses: codecov/codecov-action@v3
|
uses: codecov/codecov-action@v3
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos
|
token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos
|
||||||
|
|
2
.github/workflows/golangci-lint.yml
vendored
2
.github/workflows/golangci-lint.yml
vendored
|
@ -21,7 +21,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/setup-go@v3
|
- uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
go-version: '1.22'
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v3
|
uses: golangci/golangci-lint-action@v3
|
||||||
|
|
2
.github/workflows/sonarqube.yml
vendored
2
.github/workflows/sonarqube.yml
vendored
|
@ -29,7 +29,7 @@ jobs:
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.21.x'
|
go-version: '1.22.x'
|
||||||
|
|
||||||
- name: Run unit Tests
|
- name: Run unit Tests
|
||||||
run: |
|
run: |
|
||||||
|
|
33
eml.go
33
eml.go
|
@ -178,9 +178,6 @@ func parseEMLHeaders(mh *nm.Header, m *Msg) error {
|
||||||
for _, h := range commonHeaders {
|
for _, h := range commonHeaders {
|
||||||
if v := mh.Get(h.String()); v != "" {
|
if v := mh.Get(h.String()); v != "" {
|
||||||
m.SetGenHeader(h, v)
|
m.SetGenHeader(h, v)
|
||||||
if strings.EqualFold(h.String(), "subject") {
|
|
||||||
fmt.Printf("SUBJECT: %s\n", m.GetGenHeader(HeaderSubject)[0])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,7 +201,8 @@ func parseEMLBodyParts(pm *nm.Message, bodybuf *bytes.Buffer, m *Msg) error {
|
||||||
if err := parseEMLBodyPlain(mediatype, pm, bodybuf, m); err != nil {
|
if err := parseEMLBodyPlain(mediatype, pm, bodybuf, m); err != nil {
|
||||||
return fmt.Errorf("failed to parse plain body: %w", err)
|
return fmt.Errorf("failed to parse plain body: %w", err)
|
||||||
}
|
}
|
||||||
case strings.EqualFold(mediatype, TypeMultipartAlternative.String()):
|
case strings.EqualFold(mediatype, TypeMultipartAlternative.String()),
|
||||||
|
strings.EqualFold(mediatype, "multipart/mixed"):
|
||||||
if err := parseEMLMultipartAlternative(params, bodybuf, m); err != nil {
|
if err := parseEMLMultipartAlternative(params, bodybuf, m); err != nil {
|
||||||
return fmt.Errorf("failed to parse multipart/alternative body: %w", err)
|
return fmt.Errorf("failed to parse multipart/alternative body: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -266,19 +264,27 @@ func parseEMLMultipartAlternative(params map[string]string, bodybuf *bytes.Buffe
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("failed to get content-type from part")
|
return fmt.Errorf("failed to get content-type from part")
|
||||||
}
|
}
|
||||||
mpContentTypeSplit := strings.Split(mpContentType[0], "; ")
|
conType, charSet := parseContentType(mpContentType[0])
|
||||||
p := m.newPart(ContentType(mpContentTypeSplit[0]))
|
p := m.newPart(ContentType(conType))
|
||||||
parseEMLMultiPartCharset(mpContentTypeSplit, p)
|
p.SetCharset(Charset(charSet))
|
||||||
|
|
||||||
mpTransferEnc, ok := mpart.Header[HeaderContentTransferEnc.String()]
|
mpTransferEnc, ok := mpart.Header[HeaderContentTransferEnc.String()]
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("failed to get content-transfer-encoding from part")
|
// If CTE is empty we can assume that it's a quoted-printable CTE since the
|
||||||
|
// GO stdlib multipart packages deletes that header
|
||||||
|
// See: https://cs.opensource.google/go/go/+/refs/tags/go1.22.0:src/mime/multipart/multipart.go;l=161
|
||||||
|
mpTransferEnc = []string{EncodingQP.String()}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case strings.EqualFold(mpTransferEnc[0], EncodingB64.String()):
|
case strings.EqualFold(mpTransferEnc[0], EncodingB64.String()):
|
||||||
if err := handleEMLMultiPartBase64Encoding(mpdata, p); err != nil {
|
if err := handleEMLMultiPartBase64Encoding(mpdata, p); err != nil {
|
||||||
return fmt.Errorf("failed to handle multipart base64 transfer-encoding: %w", err)
|
return fmt.Errorf("failed to handle multipart base64 transfer-encoding: %w", err)
|
||||||
}
|
}
|
||||||
|
case strings.EqualFold(mpTransferEnc[0], EncodingQP.String()):
|
||||||
|
p.SetContent(string(mpdata))
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported Content-Transfer-Encoding")
|
||||||
}
|
}
|
||||||
|
|
||||||
m.parts = append(m.parts, p)
|
m.parts = append(m.parts, p)
|
||||||
|
@ -291,17 +297,6 @@ func parseEMLMultipartAlternative(params map[string]string, bodybuf *bytes.Buffe
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseEMLMultiPartCharset parses the Charset from a ContentType header and assigns it to a Part
|
|
||||||
// TODO: This might be redundant to parseContentType
|
|
||||||
func parseEMLMultiPartCharset(mpContentTypeSplit []string, p *Part) {
|
|
||||||
if len(mpContentTypeSplit) > 1 && strings.HasPrefix(strings.ToLower(mpContentTypeSplit[1]), "charset=") {
|
|
||||||
valSplit := strings.Split(mpContentTypeSplit[1], "=")
|
|
||||||
if len(valSplit) > 1 {
|
|
||||||
p.SetCharset(Charset(valSplit[1]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// handleEMLMultiPartBase64Encoding sets the content body of a base64 encoded Part
|
// handleEMLMultiPartBase64Encoding sets the content body of a base64 encoded Part
|
||||||
func handleEMLMultiPartBase64Encoding(mpdata []byte, p *Part) error {
|
func handleEMLMultiPartBase64Encoding(mpdata []byte, p *Part) error {
|
||||||
p.SetEncoding(EncodingB64)
|
p.SetEncoding(EncodingB64)
|
||||||
|
|
|
@ -140,6 +140,8 @@ const (
|
||||||
const (
|
const (
|
||||||
TypeAppOctetStream ContentType = "application/octet-stream"
|
TypeAppOctetStream ContentType = "application/octet-stream"
|
||||||
TypeMultipartAlternative ContentType = "multipart/alternative"
|
TypeMultipartAlternative ContentType = "multipart/alternative"
|
||||||
|
TypeMultipartMixed ContentType = "multipart/mixed"
|
||||||
|
TypeMultipartRelated ContentType = "multipart/related"
|
||||||
TypePGPSignature ContentType = "application/pgp-signature"
|
TypePGPSignature ContentType = "application/pgp-signature"
|
||||||
TypePGPEncrypted ContentType = "application/pgp-encrypted"
|
TypePGPEncrypted ContentType = "application/pgp-encrypted"
|
||||||
TypeTextHTML ContentType = "text/html"
|
TypeTextHTML ContentType = "text/html"
|
||||||
|
|
|
@ -40,6 +40,18 @@ func TestContentType_String(t *testing.T) {
|
||||||
"ContentType: application/octet-stream", TypeAppOctetStream,
|
"ContentType: application/octet-stream", TypeAppOctetStream,
|
||||||
"application/octet-stream",
|
"application/octet-stream",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ContentType: multipart/alternative", TypeMultipartAlternative,
|
||||||
|
"multipart/alternative",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ContentType: multipart/mixed", TypeMultipartMixed,
|
||||||
|
"multipart/mixed",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ContentType: multipart/related", TypeMultipartRelated,
|
||||||
|
"multipart/related",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ContentType: application/pgp-signature", TypePGPSignature,
|
"ContentType: application/pgp-signature", TypePGPSignature,
|
||||||
"application/pgp-signature",
|
"application/pgp-signature",
|
||||||
|
|
|
@ -89,11 +89,11 @@ func (mw *msgWriter) writeMsg(m *Msg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.hasMixed() {
|
if m.hasMixed() {
|
||||||
mw.startMP("mixed", m.boundary)
|
mw.startMP(MIMEMixed, m.boundary)
|
||||||
mw.writeString(DoubleNewLine)
|
mw.writeString(DoubleNewLine)
|
||||||
}
|
}
|
||||||
if m.hasRelated() {
|
if m.hasRelated() {
|
||||||
mw.startMP("related", m.boundary)
|
mw.startMP(MIMERelated, m.boundary)
|
||||||
mw.writeString(DoubleNewLine)
|
mw.writeString(DoubleNewLine)
|
||||||
}
|
}
|
||||||
if m.hasAlt() {
|
if m.hasAlt() {
|
||||||
|
|
Loading…
Reference in a new issue