Skip to content

Commit

Permalink
Merge branch 'release/v0.0.5'
Browse files Browse the repository at this point in the history
  • Loading branch information
axllent committed Jul 9, 2024
2 parents fe88d67 + 8db9992 commit e70f2e5
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 34 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/close-stale-issues.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v8.0.0
- uses: actions/stale@v9.0.0
with:
days-before-issue-stale: 21
days-before-issue-close: 7
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
Expand All @@ -56,7 +56,7 @@ jobs:
# 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)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
uses: github/codeql-action/autobuild@v3

# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
Expand All @@ -69,4 +69,4 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3
2 changes: 1 addition & 1 deletion .github/workflows/release-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- uses: actions/checkout@v4

# build the binaries
- uses: wangyoucao577/go-release-action@v1.40
- uses: wangyoucao577/go-release-action@v1.51
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
goos: ${{ matrix.goos }}
Expand Down
99 changes: 76 additions & 23 deletions cmd/mailer.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
package cmd

import (
"bytes"
"crypto/tls"
"fmt"
"io"
"net/mail"
"net/smtp"
"os"
"regexp"
"strconv"
"strings"
"time"

"github.com/lithammer/shortuuid"
)

// SMTP wrapper will send and optionally log the transaction
func smtpWrapper(from string, to []string, msg []byte) error {
func smtpWrapper(from string, to []string, message []byte) error {
msg, err := injectHeaders(message)
if err != nil {
return err
}

code, response, err := smtpSend(from, to, msg)

if config.LogFile != "" {
Expand All @@ -21,11 +31,15 @@ func smtpWrapper(from string, to []string, msg []byte) error {
if config.STARTTLS {
tls = "on"
}
if err != nil {
code, response = smtpErrParser(err.Error())
}

recipients := strings.Join(to, ",")
logMsg := fmt.Sprintf("%s host=%s tls=%s from=%s, recipients=%s mailsize=%d, smtpstatus=%d smtpmsg='%s'\n", ts, config.SMTPHost, tls, from, recipients, len(msg), code, response)

file, err := os.OpenFile(config.LogFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666) // #nosec
// silently fail if the file cannot be opened
// silently fail if the log file cannot be opened
if err == nil {
defer file.Close()
_, _ = file.Write([]byte(logMsg))
Expand Down Expand Up @@ -75,6 +89,7 @@ func smtpSend(from string, to []string, msg []byte) (int, string, error) {
return 0, "", err
}
}

if err = c.Mail(from); err != nil {
return 0, "", err
}
Expand All @@ -85,41 +100,79 @@ func smtpSend(from string, to []string, msg []byte) (int, string, error) {
}
}

w, err := c.Data()
code, message, err := dataWithResponse(c, msg)
if err != nil {
return 0, "", err
}

if _, err := w.Write(msg); err != nil {
return code, message, c.Quit()
}

// Go's net/smtp Data() only returns an error message. This custom function
// also returns the server's response message so it can be logged.
//
// @see https://github.com/axllent/sndmail/issues/10#issuecomment-2214807859
func dataWithResponse(c *smtp.Client, msg []byte) (int, string, error) {
id, err := c.Text.Cmd("DATA")
if err != nil {
return 0, "", err
}

defer c.Quit()
c.Text.StartResponse(id)

code, message, err := closeData(c)
code, message, err := c.Text.ReadResponse(354)
if err != nil {
return code, message, err
}

return code, message, err
}
c.Text.EndResponse(id)

w := c.Text.DotWriter()

// CloseData wil ensure the SMTP server response is returned
// @see https://stackoverflow.com/a/70925659
func closeData(client *smtp.Client) (int, string, error) {
d := &dataCloser{
c: client,
WriteCloser: client.Text.DotWriter(),
if _, err := w.Write(msg); err != nil {
return 0, "", err
}

if err := w.Close(); err != nil {
return 0, "", err
}

return d.Close()
return c.Text.ReadResponse(250)
}

type dataCloser struct {
c *smtp.Client
io.WriteCloser
// Inject Message-Id and Date if missing
func injectHeaders(body []byte) ([]byte, error) {
msg, err := mail.ReadMessage(bytes.NewReader(body))
if err != nil {
return nil, err
}

// add message ID if missing
if msg.Header.Get("Message-Id") == "" {
messageID := shortuuid.New() + "@sndmail"
body = append([]byte("Message-Id: <"+messageID+">\r\n"), body...)
}

// add date if missing
if msg.Header.Get("Date") == "" {
now := time.Now().Format("Mon, 02 Jan 2006 15:04:05 -0700 (MST)")
body = append([]byte("Date: "+now+"\r\n"), body...)
}

return body, nil
}

func (d *dataCloser) Close() (int, string, error) {
d.WriteCloser.Close() // #nosec make sure WriterCloser gets closed
code, message, err := d.c.Text.ReadResponse(250)
// error parser for SMTP response messages
func smtpErrParser(s string) (int, string) {
var re = regexp.MustCompile(`(\d\d\d) (.*)`)
if re.MatchString(s) {
matches := re.FindAllStringSubmatch(s, -1)
for _, m := range matches {
i, _ := strconv.Atoi(m[1])

return i, m[2]
}
}

return code, message, err
return 0, s
}
4 changes: 0 additions & 4 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,3 @@ func Exec() {
sendmail()
}
}

// func main() {
// Cmd()
// }
2 changes: 1 addition & 1 deletion cmd/sendmail.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func sendmail() {

msg, err := mail.ReadMessage(bytes.NewReader(body))
if err != nil {
// create blank message to lookups don't fail
// create blank message so lookups don't fail
msg = &mail.Message{}

// inject a new blank line below body
Expand Down
7 changes: 6 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,9 @@ module github.com/axllent/sndmail

go 1.19

require github.com/spf13/pflag v1.0.5
require (
github.com/lithammer/shortuuid v3.0.0+incompatible
github.com/spf13/pflag v1.0.5
)

require github.com/google/uuid v1.6.0 // indirect
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/lithammer/shortuuid v3.0.0+incompatible h1:NcD0xWW/MZYXEHa6ITy6kaXN5nwm/V115vj2YXfhS0w=
github.com/lithammer/shortuuid v3.0.0+incompatible/go.mod h1:FR74pbAuElzOUuenUHTK2Tciko1/vKuIKS9dSkDrA4w=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=

0 comments on commit e70f2e5

Please sign in to comment.