mirror of
https://github.com/wneessen/go-mail.git
synced 2024-11-22 13:50:49 +01:00
More progress... calling it a day.
This commit is contained in:
parent
4b4c8065f6
commit
6285b5fb4f
6 changed files with 303 additions and 10 deletions
112
.idea/workspace.xml
Normal file
112
.idea/workspace.xml
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AutoImportSettings">
|
||||||
|
<option name="autoReloadType" value="ALL" />
|
||||||
|
</component>
|
||||||
|
<component name="ChangeListManager">
|
||||||
|
<list default="true" id="b7733b42-c5ae-46b3-8a96-8b973d69a11d" name="Changes" comment="Calling it a day...">
|
||||||
|
<change afterPath="$PROJECT_DIR$/encoding.go" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/header.go" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/mailmsg.go" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/client_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/client_test.go" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/cmd/main.go" beforeDir="false" afterPath="$PROJECT_DIR$/cmd/main.go" afterDir="false" />
|
||||||
|
</list>
|
||||||
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
|
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||||
|
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||||
|
</component>
|
||||||
|
<component name="FileTemplateManagerImpl">
|
||||||
|
<option name="RECENT_TEMPLATES">
|
||||||
|
<list>
|
||||||
|
<option value="Go File" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="GOROOT" url="file://$PROJECT_DIR$/../../go1.17.8" />
|
||||||
|
<component name="Git.Settings">
|
||||||
|
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||||
|
</component>
|
||||||
|
<component name="MarkdownSettingsMigration">
|
||||||
|
<option name="stateVersion" value="1" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectId" id="25y1BKo9v06G2DMj9UoZG0ZxDb1" />
|
||||||
|
<component name="ProjectLevelVcsManager">
|
||||||
|
<OptionsSetting value="false" id="Update" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectViewState">
|
||||||
|
<option name="hideEmptyMiddlePackages" value="true" />
|
||||||
|
<option name="showLibraryContents" value="true" />
|
||||||
|
</component>
|
||||||
|
<component name="PropertiesComponent">
|
||||||
|
<property name="DefaultGoTemplateProperty" value="Go File" />
|
||||||
|
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
|
||||||
|
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
|
||||||
|
<property name="TF_FMT" value="false" />
|
||||||
|
<property name="WebServerToolWindowFactoryState" value="true" />
|
||||||
|
<property name="go.format.on.save.advertiser.fired" value="true" />
|
||||||
|
<property name="go.formatter.settings.were.checked" value="true" />
|
||||||
|
<property name="go.import.settings.migrated" value="true" />
|
||||||
|
<property name="go.modules.go.list.on.any.changes.was.set" value="true" />
|
||||||
|
<property name="go.sdk.automatically.set" value="true" />
|
||||||
|
<property name="go.watchers.conflict.with.on.save.actions.check.performed" value="true" />
|
||||||
|
<property name="last_opened_file_path" value="$USER_HOME$" />
|
||||||
|
<property name="settings.editor.selected.configurable" value="go.vgo" />
|
||||||
|
</component>
|
||||||
|
<component name="RunManager">
|
||||||
|
<configuration default="true" type="GoApplicationRunConfiguration" factoryName="Go Application">
|
||||||
|
<module name="go-mail" />
|
||||||
|
<working_directory value="$PROJECT_DIR$" />
|
||||||
|
<go_parameters value="-i" />
|
||||||
|
<kind value="PACKAGE" />
|
||||||
|
<package value="go-mail" />
|
||||||
|
<directory value="$PROJECT_DIR$" />
|
||||||
|
<filePath value="$PROJECT_DIR$" />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
<configuration default="true" type="GoTestRunConfiguration" factoryName="Go Test">
|
||||||
|
<module name="go-mail" />
|
||||||
|
<working_directory value="$PROJECT_DIR$" />
|
||||||
|
<go_parameters value="-i" />
|
||||||
|
<kind value="DIRECTORY" />
|
||||||
|
<package value="go-mail" />
|
||||||
|
<directory value="$PROJECT_DIR$" />
|
||||||
|
<filePath value="$PROJECT_DIR$" />
|
||||||
|
<framework value="gotest" />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
<configuration name="go test github.com/wneessen/go-mail" type="GoTestRunConfiguration" factoryName="Go Test" nameIsGenerated="true">
|
||||||
|
<module name="go-mail" />
|
||||||
|
<working_directory value="$PROJECT_DIR$" />
|
||||||
|
<kind value="PACKAGE" />
|
||||||
|
<package value="github.com/wneessen/go-mail" />
|
||||||
|
<directory value="$PROJECT_DIR$" />
|
||||||
|
<filePath value="$PROJECT_DIR$" />
|
||||||
|
<framework value="gotest" />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
|
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||||
|
<component name="TypeScriptGeneratedFilesManager">
|
||||||
|
<option name="version" value="3" />
|
||||||
|
</component>
|
||||||
|
<component name="Vcs.Log.Tabs.Properties">
|
||||||
|
<option name="TAB_STATES">
|
||||||
|
<map>
|
||||||
|
<entry key="MAIN">
|
||||||
|
<value>
|
||||||
|
<State />
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="VcsManagerConfiguration">
|
||||||
|
<MESSAGE value="Makeing some progress..." />
|
||||||
|
<MESSAGE value="Calling it a day..." />
|
||||||
|
<option name="LAST_COMMIT_MESSAGE" value="Calling it a day..." />
|
||||||
|
</component>
|
||||||
|
<component name="VgoProject">
|
||||||
|
<integration-enabled>true</integration-enabled>
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -101,3 +101,54 @@ func TestWithSSL(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestWithTLSPolicy tests the WithTLSPolicy() option for the NewClient() method
|
||||||
|
func TestWithTLSPolicy(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
value TLSPolicy
|
||||||
|
want TLSPolicy
|
||||||
|
}{
|
||||||
|
{"Policy: TLSMandatory", TLSMandatory, TLSMandatory},
|
||||||
|
{"Policy: TLSOpportunistic", TLSOpportunistic, TLSOpportunistic},
|
||||||
|
{"Policy: NoTLS", NoTLS, NoTLS},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
c, err := NewClient(DefaultHost, WithTLSPolicy(tt.value))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to create new client: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.tlspolicy != tt.want {
|
||||||
|
t.Errorf("failed to set TLSPolicy. Want: %s, got: %s", tt.want, c.tlspolicy)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSetTLSPolicy tests the SetTLSPolicy() method for the Client object
|
||||||
|
func TestSetTLSPolicy(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
value TLSPolicy
|
||||||
|
want TLSPolicy
|
||||||
|
}{
|
||||||
|
{"Policy: TLSMandatory", TLSMandatory, TLSMandatory},
|
||||||
|
{"Policy: TLSOpportunistic", TLSOpportunistic, TLSOpportunistic},
|
||||||
|
{"Policy: NoTLS", NoTLS, NoTLS},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
c, err := NewClient(DefaultHost, WithTLSPolicy(NoTLS))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to create new client: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.SetTLSPolicy(tt.value)
|
||||||
|
if c.tlspolicy != tt.want {
|
||||||
|
t.Errorf("failed to set TLSPolicy. Want: %s, got: %s", tt.want, c.tlspolicy)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
20
cmd/main.go
20
cmd/main.go
|
@ -2,7 +2,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/wneessen/go-mail"
|
"github.com/wneessen/go-mail"
|
||||||
"os"
|
"os"
|
||||||
|
@ -15,18 +14,12 @@ func main() {
|
||||||
fmt.Printf("$TEST_HOST env variable cannot be empty\n")
|
fmt.Printf("$TEST_HOST env variable cannot be empty\n")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
c, err := mail.NewClient(th, mail.WithTimeout(time.Millisecond*500), mail.WithTLSPolicy(mail.TLSOpportunistic))
|
c, err := mail.NewClient(th, mail.WithTimeout(time.Millisecond*500))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("failed to create new client: %s\n", err)
|
fmt.Printf("failed to create new client: %s\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
//c.SetTLSPolicy(mail.TLSMandatory)
|
//c.SetTLSPolicy(mail.TLSMandatory)
|
||||||
tc := &tls.Config{
|
|
||||||
ServerName: th,
|
|
||||||
MinVersion: tls.VersionTLS10,
|
|
||||||
MaxVersion: tls.VersionTLS10,
|
|
||||||
}
|
|
||||||
c.SetTLSConfig(tc)
|
|
||||||
|
|
||||||
ctx, cfn := context.WithCancel(context.Background())
|
ctx, cfn := context.WithCancel(context.Background())
|
||||||
defer cfn()
|
defer cfn()
|
||||||
|
@ -35,6 +28,13 @@ func main() {
|
||||||
fmt.Printf("failed to dial: %s\n", err)
|
fmt.Printf("failed to dial: %s\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
fmt.Printf("Client: %+v\n", c)
|
|
||||||
fmt.Printf("StartTLS policy: %s\n", c.TLSPolicy())
|
m := mail.NewMsg()
|
||||||
|
m.From("wn@neessen.net")
|
||||||
|
m.To("test@test.de", "foo@bar.de", "blubb@blah.com")
|
||||||
|
m.Cc("cc@test.de", "cc@bar.de", "cc@blah.com")
|
||||||
|
m.SetMessageID()
|
||||||
|
m.SetBulk()
|
||||||
|
m.Header()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
15
encoding.go
Normal file
15
encoding.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package mail
|
||||||
|
|
||||||
|
// Encoding represents a MIME encoding scheme like quoted-printable or base64.
|
||||||
|
type Encoding string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// EncodingB64 represents the Base64 encoding as specified in RFC 2045.
|
||||||
|
EncodingB64 Encoding = "base64"
|
||||||
|
|
||||||
|
// EncodingQP represents the "quoted-printable" encoding as specified in RFC 2045.
|
||||||
|
EncodingQP Encoding = "quoted-printable"
|
||||||
|
|
||||||
|
// NoEncoding avoids any character encoding (except of the mail headers)
|
||||||
|
NoEncoding Encoding = "8bit"
|
||||||
|
)
|
27
header.go
Normal file
27
header.go
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package mail
|
||||||
|
|
||||||
|
// Header represents a mail header field name
|
||||||
|
type Header string
|
||||||
|
|
||||||
|
// List of common header field names
|
||||||
|
const (
|
||||||
|
// HeaderFrom is the "From" header field
|
||||||
|
HeaderFrom Header = "From"
|
||||||
|
|
||||||
|
// HeaderTo is the "Receipient" header field
|
||||||
|
HeaderTo Header = "To"
|
||||||
|
|
||||||
|
// HeaderCc is the "Carbon Copy" header field
|
||||||
|
HeaderCc Header = "Cc"
|
||||||
|
|
||||||
|
// HeaderPrecedence is the "Precedence" header field
|
||||||
|
HeaderPrecedence Header = "Precedence"
|
||||||
|
|
||||||
|
// HeaderDate represents the "Date" field
|
||||||
|
// See: https://www.rfc-editor.org/rfc/rfc822#section-5.1
|
||||||
|
HeaderDate Header = "Date"
|
||||||
|
|
||||||
|
// HeaderMessageID represents the "Message-ID" field for message identification
|
||||||
|
// See: https://www.rfc-editor.org/rfc/rfc1036#section-2.1.5
|
||||||
|
HeaderMessageID Header = "Message-ID"
|
||||||
|
)
|
88
mailmsg.go
Normal file
88
mailmsg.go
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
package mail
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"mime"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Msg is the mail message struct
|
||||||
|
type Msg struct {
|
||||||
|
// charset represents the charset of the mail (defaults to UTF-8)
|
||||||
|
charset string
|
||||||
|
|
||||||
|
// encoder represents a mime.WordEncoder from the std lib
|
||||||
|
encoder mime.WordEncoder
|
||||||
|
|
||||||
|
// header is a slice of strings that the different mail header fields
|
||||||
|
header map[Header][]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsg returns a new Msg pointer
|
||||||
|
func NewMsg() *Msg {
|
||||||
|
m := &Msg{
|
||||||
|
charset: "UTF-8",
|
||||||
|
header: make(map[Header][]string),
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetHeader sets a header field of the Msg
|
||||||
|
func (m *Msg) SetHeader(h Header, v ...string) {
|
||||||
|
switch h {
|
||||||
|
case HeaderFrom:
|
||||||
|
m.header[h] = []string{v[0]}
|
||||||
|
default:
|
||||||
|
m.header[h] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// From sets the From: address of the Msg
|
||||||
|
func (m *Msg) From(f string) {
|
||||||
|
m.SetHeader(HeaderFrom, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// To sets the To: addresses of the Msg
|
||||||
|
func (m *Msg) To(t ...string) {
|
||||||
|
m.SetHeader(HeaderTo, t...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cc sets the Cc: addresses of the Msg
|
||||||
|
func (m *Msg) Cc(c ...string) {
|
||||||
|
m.SetHeader(HeaderCc, c...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMessageID generates a random message id for the mail
|
||||||
|
func (m *Msg) SetMessageID() {
|
||||||
|
hn, err := os.Hostname()
|
||||||
|
if err != nil {
|
||||||
|
hn = "localhost.localdomain"
|
||||||
|
}
|
||||||
|
ct := time.Now().UnixMicro()
|
||||||
|
r := rand.New(rand.NewSource(ct))
|
||||||
|
rn := r.Int()
|
||||||
|
pid := os.Getpid()
|
||||||
|
|
||||||
|
mid := fmt.Sprintf("%d.%d.%d@%s", pid, rn, ct, hn)
|
||||||
|
m.SetMessageIDWithValue(mid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMessageIDWithValue sets the message id for the mail
|
||||||
|
func (m *Msg) SetMessageIDWithValue(v string) {
|
||||||
|
m.SetHeader(HeaderMessageID, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBulk sets the "Precedense: bulk" header which is recommended for
|
||||||
|
// automated mails like OOO replies
|
||||||
|
// See: https://www.rfc-editor.org/rfc/rfc2076#section-3.9
|
||||||
|
func (m *Msg) SetBulk() {
|
||||||
|
m.SetHeader(HeaderPrecedence, "bulk")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Header FixMe
|
||||||
|
func (m *Msg) Header() {
|
||||||
|
fmt.Printf("%+v\n", m.header)
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue