Skip to content

Commit

Permalink
Add reply-to functionality in email sending and a way to see current …
Browse files Browse the repository at this point in the history
…version
  • Loading branch information
JCoupalK committed Feb 14, 2025
1 parent d1e87f3 commit aceed97
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 74 deletions.
17 changes: 11 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,16 @@ Mail2Go is a very lightweight command-line SMTP client written in Go, designed t

## Installation

1. Download the Linux amd64 binary with wget (more versions on [release](https://github.com/KeepSec-Technologies/Mail2Go/releases/tag/1.1.7) tab):
1. Download the Linux amd64 binary with wget (more versions on [release](https://github.com/KeepSec-Technologies/Mail2Go/releases/tag/1.1.8) tab):

```shell
wget https://github.com/KeepSec-Technologies/Mail2Go/releases/download/1.1.7/mail2go_linux_amd64_1.1.7.tar.gz
wget https://github.com/KeepSec-Technologies/Mail2Go/releases/download/1.1.8/mail2go_linux_amd64_1.1.8.tar.gz
```

2. Unpack it with tar

```shell
tar -xf mail2go_linux_amd64_1.1.7.tar.gz
tar -xf mail2go_linux_amd64_1.1.8.tar.gz
```

3. Move it to your /usr/local/bin/ (Optional):
Expand Down Expand Up @@ -91,11 +91,13 @@ Run the Mail2Go tool with the required arguments:
-c, --config Path to the SMTP json config file which replaces the above arguments
-t, --to-email Email addresses that will receive the email, can be multiples (comma-separated)
-t, --to-email Email addresses that will receive the email, comma-separated
-r, --reply-to Email address to reply to (optional)
-h, --subject Subject of the email
-b, --body Body of the email
-af, --attachments File paths for attachments, can be multiples (comma-separated)
-bf, --body-file File path for email body
-af, --attachments File paths for attachments, comma-separated (optional)
-bf, --body-file File path for HTML email body (replaces the --body argument)
-v, --version Application version
```

## Examples
Expand All @@ -107,6 +109,9 @@ mail2go -s mail.example.com -p 587 -u [email protected] -w password123 -l tls -f
# Example with two recipients, the body from an HTML file and two attached files (can be more):
mail2go -s mail.example.com -p 587 -u [email protected] -w password123 -l tls -f [email protected] -t [email protected],[email protected] -h 'Test Mail2Go Subject' -bf demo/body.html -af README.md,demo/mail2go-smaller.png
# Example with a reply-to address:
mail2go -s mail.example.com -p 587 -u [email protected] -w password123 -l tls -f [email protected] -t [email protected] -r [email protected] -h 'Test Mail2Go Subject' -b 'This is a body!'
# Example without authentication and no TLS:
mail2go -s mail.example.com -p 25 -l none -f [email protected] -t [email protected] -h 'Test Mail2Go Subject' -b 'This is a body!'
Expand Down
29 changes: 25 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"strings"
)

var version = "1.1.8"

var (
// Long-form flags
smtpServer string
Expand All @@ -21,34 +23,40 @@ var (

fromEmail string
toEmail string
replyTo string

subject string
body string

attachmentsFiles string
bodyFile string

showVersion bool

// Short-form flags
smtpServerShort string
smtpPortShort int
usernameShort string
passwordShort string

noAuth bool
noAuthShort bool
noAuth bool
noAuthShort bool

tlsModeShort string

configFileShort string

fromEmailShort string
toEmailShort string
replyToShort string

subjectShort string
bodyShort string

attachmentsFilesShort string
bodyFileShort string

showVersionShort bool
)

func init() {
Expand All @@ -65,13 +73,16 @@ func init() {

flag.StringVar(&fromEmail, "from-email", "", "Email address to send from")
flag.StringVar(&toEmail, "to-email", "", "Email addresses that will receive the email, comma-separated")
flag.StringVar(&replyTo, "reply-to", "", "Email address to reply to")

flag.StringVar(&subject, "subject", "", "Subject of the email")
flag.StringVar(&body, "body", "", "Body of the email")

flag.StringVar(&attachmentsFiles, "attachments", "", "File paths for attachments, comma-separated")
flag.StringVar(&bodyFile, "body-file", "", "File path for email body")

flag.BoolVar(&showVersion, "version", false, "Display application version")

// Short-form flags
flag.StringVar(&smtpServerShort, "s", "", "SMTP server for sending emails (short)")
flag.IntVar(&smtpPortShort, "p", 587, "SMTP server port (short)")
Expand All @@ -85,19 +96,28 @@ func init() {

flag.StringVar(&fromEmailShort, "f", "", "Email address to send from (short)")
flag.StringVar(&toEmailShort, "t", "", "Email addresses that will receive the email, comma-separated (short)")
flag.StringVar(&replyToShort, "r", "", "Email address to reply to (short)")

flag.StringVar(&subjectShort, "h", "", "Subject of the email (short)")
flag.StringVar(&bodyShort, "b", "", "Body of the email (short)")

flag.StringVar(&attachmentsFilesShort, "af", "", "File paths for attachments, comma-separated (short)")
flag.StringVar(&bodyFileShort, "bf", "", "File path for email body (short)")

flag.BoolVar(&showVersionShort, "v", false, "Display application version")
}

func main() {
// Override the default flag.Usage
flag.Usage = Usage
flag.Parse()

showVersion = showVersion || showVersionShort
if showVersion {
fmt.Printf("Mail2Go Version: %s\n", version)
os.Exit(0)
}

var config Config = Config{}

// Load config file
Expand All @@ -124,11 +144,12 @@ func main() {
smtpPort = priorityInt(587, []int{config.SMTPPort, smtpPort, smtpPortShort})
username = priorityString([]string{config.SMTPUsername, username, usernameShort})
password = priorityString([]string{config.SMTPPassword, password, passwordShort})
noAuth = noAuth || noAuthShort || config.NoAuth
noAuth = config.NoAuth || noAuth || noAuthShort
tlsMode = priorityString([]string{config.TLSMode, tlsMode, tlsModeShort})
fromEmail = priorityString([]string{config.FromEmail, fromEmail, fromEmailShort})

toEmail = priorityString([]string{toEmail, toEmailShort})
replyTo = priorityString([]string{replyTo, replyToShort})
subject = priorityString([]string{subject, subjectShort})
body = priorityString([]string{body, bodyShort})
attachmentsFiles = priorityString([]string{attachmentsFiles, attachmentsFilesShort})
Expand Down Expand Up @@ -172,7 +193,7 @@ func main() {
Usage()
}

sendEmail(smtpServer, smtpPort, username, password, fromEmail, toEmails, subject, body, bodyFile, attachmentPaths, tlsMode, noAuth)
sendEmail(smtpServer, smtpPort, username, password, fromEmail, toEmails, replyTo, subject, body, bodyFile, attachmentPaths, tlsMode, noAuth)
}

func priorityString(strings []string) string {
Expand Down
133 changes: 71 additions & 62 deletions smtp.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,75 +8,84 @@ import (
"github.com/wneessen/go-mail"
)

// sendEmail constructs and sends an email with the specified HTML body and attachments.
func sendEmail(smtpServer string, smtpPort int, username string, password string, from string, to []string, subject, body, bodyFile string, attachments []string, tlsMode string, noAuth bool) error {
// Create a new message
m := mail.NewMsg()
if err := m.From(from); err != nil {
return err
}
// sendEmail function modified to include the reply-to header
func sendEmail(smtpServer string, smtpPort int, username string, password string, from string, to []string, replyTo string, subject, body, bodyFile string, attachments []string, tlsMode string, noAuth bool) error {
// Create a new message
m := mail.NewMsg()
if err := m.From(from); err != nil {
return err
}

// Set recipient(s)
if err := m.To(to...); err != nil {
return err
}
// Set recipient(s)
if err := m.To(to...); err != nil {
return err
}

m.Subject(subject)
// Set the subject
m.Subject(subject)

// Set the body
if bodyFile != "" {
m.SetBodyString(mail.TypeTextHTML, body)
} else {
m.SetBodyString(mail.TypeTextPlain, body)
}
// Set the body
if bodyFile != "" {
m.SetBodyString(mail.TypeTextHTML, body)
} else {
m.SetBodyString(mail.TypeTextPlain, body)
}

// Add attachments
for _, attachment := range attachments {
m.AttachFile(attachment)
}
// Add attachments
for _, attachment := range attachments {
m.AttachFile(attachment)
}

clientOptions := []mail.Option{
mail.WithPort(smtpPort),
}
// Add the reply-to header if provided
if replyTo != "" {
if err := m.ReplyTo(replyTo); err != nil {
return err
}
}

// Define client options
if !noAuth && (username != "" || password != "") {
clientOptions = append(
clientOptions,
mail.WithSMTPAuth(mail.SMTPAuthLogin),
mail.WithUsername(username),
mail.WithPassword(password),
)
}
clientOptions := []mail.Option{
mail.WithPort(smtpPort),
}

// Conditionally add TLS options based on tlsMode
switch tlsMode {
case "none":
clientOptions = append(clientOptions, mail.WithTLSPolicy(mail.NoTLS))
case "tls-skip":
tlsSkipConfig := &tls.Config{
InsecureSkipVerify: true,
ServerName: smtpServer,
}
clientOptions = append(clientOptions, mail.WithTLSConfig(tlsSkipConfig))
case "tls":
clientOptions = append(clientOptions, mail.WithTLSPolicy(mail.TLSMandatory))
}
// Create a new client using the options
c, err := mail.NewClient(smtpServer, clientOptions...)
if err != nil {
fmt.Printf("Failed to create SMTP client: %v", err)
}
if c == nil {
fmt.Printf("SMTP client is nil")
}
// Define client options
if !noAuth && (username != "" || password != "") {
clientOptions = append(
clientOptions,
mail.WithSMTPAuth(mail.SMTPAuthLogin),
mail.WithUsername(username),
mail.WithPassword(password),
)
}

// Send the email
if err := c.DialAndSend(m); err != nil {
fmt.Printf("Error sending email: %v", err)
return err
}
// Conditionally add TLS options based on tlsMode
switch tlsMode {
case "none":
clientOptions = append(clientOptions, mail.WithTLSPolicy(mail.NoTLS))
case "tls-skip":
tlsSkipConfig := &tls.Config{
InsecureSkipVerify: true,
ServerName: smtpServer,
}
clientOptions = append(clientOptions, mail.WithTLSConfig(tlsSkipConfig))
case "tls":
clientOptions = append(clientOptions, mail.WithTLSPolicy(mail.TLSMandatory))
}

fmt.Printf("\nEmail sent successfully to %s from %s\n", strings.Join(to, ", "), from)
return nil
// Create a new client using the options
c, err := mail.NewClient(smtpServer, clientOptions...)
if err != nil {
fmt.Printf("Failed to create SMTP client: %v", err)
}
if c == nil {
fmt.Printf("SMTP client is nil")
}

// Send the email
if err := c.DialAndSend(m); err != nil {
fmt.Printf("Error sending email: %v", err)
return err
}

fmt.Printf("\nEmail sent successfully to %s from %s\n", strings.Join(to, ", "), from)
return nil
}
6 changes: 4 additions & 2 deletions usage.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ func Usage() {
fmt.Fprintln(os.Stderr, " -c, --config Path to the SMTP json config file which replaces the above arguments")
fmt.Fprintln(os.Stderr, "")
fmt.Fprintln(os.Stderr, " -t, --to-email Email addresses that will receive the email, comma-separated")
fmt.Fprintln(os.Stderr, " -r, --reply-to Email address to reply to (optional)")
fmt.Fprintln(os.Stderr, " -h, --subject Subject of the email")
fmt.Fprintln(os.Stderr, " -b, --body Body of the email")
fmt.Fprintln(os.Stderr, " -af, --attachments File paths for attachments, comma-separated")
fmt.Fprintln(os.Stderr, " -bf, --body-file File path for email body")
fmt.Fprintln(os.Stderr, " -af, --attachments File paths for attachments, comma-separated (optional)")
fmt.Fprintln(os.Stderr, " -bf, --body-file File path for HTML email body (replaces the --body argument)")
fmt.Fprintln(os.Stderr, " -v, --version Application version")
fmt.Fprintln(os.Stderr, "")
fmt.Fprintln(os.Stderr, " Ensure all required flags are provided.")
os.Exit(1)
Expand Down

0 comments on commit aceed97

Please sign in to comment.