mirror of
https://github.com/axllent/mailpit.git
synced 2025-01-08 00:39:22 +02:00
152 lines
3.8 KiB
Go
152 lines
3.8 KiB
Go
package cmd
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"net/mail"
|
|
"net/smtp"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/axllent/mailpit/internal/logger"
|
|
sendmail "github.com/axllent/mailpit/sendmail/cmd"
|
|
"github.com/spf13/cobra"
|
|
"golang.org/x/text/language"
|
|
"golang.org/x/text/message"
|
|
)
|
|
|
|
var (
|
|
ingestRecent int
|
|
)
|
|
|
|
// ingestCmd represents the ingest command
|
|
var ingestCmd = &cobra.Command{
|
|
Use: "ingest <file|folder> ...[file|folder]",
|
|
Short: "Ingest a file or folder of emails for testing",
|
|
Long: `Ingest a file or folder of emails for testing.
|
|
|
|
This command will scan the folder for emails and deliver them via SMTP to a running
|
|
Mailpit server. Each email must be a separate file (eg: Maildir format, not mbox).
|
|
The --recent flag will only consider files with a modification date within the last X days.`,
|
|
// Hidden: true,
|
|
Args: cobra.MinimumNArgs(1),
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
var count int
|
|
var total int
|
|
var per100start = time.Now()
|
|
p := message.NewPrinter(language.English)
|
|
|
|
for _, a := range args {
|
|
err := filepath.Walk(a,
|
|
func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
logger.Log().Error(err)
|
|
return nil
|
|
}
|
|
if !isFile(path) {
|
|
return nil
|
|
}
|
|
|
|
if ingestRecent > 0 && time.Since(info.ModTime()) > time.Duration(ingestRecent)*24*time.Hour {
|
|
return nil
|
|
}
|
|
|
|
f, err := os.Open(filepath.Clean(path))
|
|
if err != nil {
|
|
logger.Log().Errorf("%s: %s", path, err.Error())
|
|
return nil
|
|
}
|
|
defer f.Close() // #nosec
|
|
|
|
body, err := io.ReadAll(f)
|
|
if err != nil {
|
|
logger.Log().Errorf("%s: %s", path, err.Error())
|
|
return nil
|
|
}
|
|
|
|
msg, err := mail.ReadMessage(bytes.NewReader(body))
|
|
if err != nil {
|
|
logger.Log().Errorf("error parsing message body: %s", err.Error())
|
|
return nil
|
|
}
|
|
|
|
recipients := []string{}
|
|
// get all recipients in To, Cc and Bcc
|
|
if to, err := msg.Header.AddressList("To"); err == nil {
|
|
for _, a := range to {
|
|
recipients = append(recipients, a.Address)
|
|
}
|
|
}
|
|
if cc, err := msg.Header.AddressList("Cc"); err == nil {
|
|
for _, a := range cc {
|
|
recipients = append(recipients, a.Address)
|
|
}
|
|
}
|
|
if bcc, err := msg.Header.AddressList("Bcc"); err == nil {
|
|
for _, a := range bcc {
|
|
recipients = append(recipients, a.Address)
|
|
}
|
|
}
|
|
|
|
if sendmail.FromAddr == "" {
|
|
if fromAddresses, err := msg.Header.AddressList("From"); err == nil {
|
|
sendmail.FromAddr = fromAddresses[0].Address
|
|
}
|
|
}
|
|
|
|
if len(recipients) == 0 {
|
|
// Bcc
|
|
recipients = []string{sendmail.FromAddr}
|
|
}
|
|
|
|
returnPath := strings.Trim(msg.Header.Get("Return-Path"), "<>")
|
|
if returnPath == "" {
|
|
if fromAddresses, err := msg.Header.AddressList("From"); err == nil {
|
|
returnPath = fromAddresses[0].Address
|
|
}
|
|
}
|
|
|
|
err = smtp.SendMail(sendmail.SMTPAddr, nil, returnPath, recipients, body)
|
|
if err != nil {
|
|
logger.Log().Errorf("error sending mail: %s (%s)", err.Error(), path)
|
|
return nil
|
|
}
|
|
|
|
count++
|
|
total++
|
|
if count%100 == 0 {
|
|
formatted := p.Sprintf("%d", total)
|
|
logger.Log().Infof("[%s] 100 messages in %s", formatted, time.Since(per100start))
|
|
|
|
per100start = time.Now()
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
logger.Log().Error(err)
|
|
}
|
|
}
|
|
|
|
},
|
|
}
|
|
|
|
func init() {
|
|
rootCmd.AddCommand(ingestCmd)
|
|
|
|
ingestCmd.Flags().StringVarP(&sendmail.SMTPAddr, "smtp-addr", "S", sendmail.SMTPAddr, "SMTP server address")
|
|
ingestCmd.Flags().IntVarP(&ingestRecent, "recent", "r", 0, "Only ingest messages from the last X days (default all)")
|
|
}
|
|
|
|
// IsFile returns if a path is a file
|
|
func isFile(path string) bool {
|
|
info, err := os.Stat(path)
|
|
if os.IsNotExist(err) || !info.Mode().IsRegular() {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|