Calling it a day...

This commit is contained in:
Winni Neessen 2022-03-10 16:56:41 +01:00
parent 57ebb171c5
commit 98d7982738
Signed by: wneessen
GPG key ID: 385AC9889632126E
5 changed files with 55 additions and 29 deletions

View file

@ -4,13 +4,12 @@
<option name="autoReloadType" value="ALL" /> <option name="autoReloadType" value="ALL" />
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="b79e8e7a-d892-4ce4-8bf4-f9e45415b803" name="Changes" comment="Better context and connection handling"> <list default="true" id="b79e8e7a-d892-4ce4-8bf4-f9e45415b803" name="Changes" comment="Lots of cleanups and refactoring">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" /> <change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/client.go" beforeDir="false" afterPath="$PROJECT_DIR$/client.go" afterDir="false" /> <change beforePath="$PROJECT_DIR$/client.go" beforeDir="false" afterPath="$PROJECT_DIR$/client.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/cmd/main.go" beforeDir="false" afterPath="$PROJECT_DIR$/cmd/main.go" afterDir="false" /> <change beforePath="$PROJECT_DIR$/cmd/main.go" beforeDir="false" afterPath="$PROJECT_DIR$/cmd/main.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/header.go" beforeDir="false" afterPath="$PROJECT_DIR$/header.go" afterDir="false" /> <change beforePath="$PROJECT_DIR$/header.go" beforeDir="false" afterPath="$PROJECT_DIR$/header.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/mailmsg.go" beforeDir="false" afterPath="$PROJECT_DIR$/mailmsg.go" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -97,7 +96,8 @@
<MESSAGE value="Progress" /> <MESSAGE value="Progress" />
<MESSAGE value="Implemented SMTP AUTH" /> <MESSAGE value="Implemented SMTP AUTH" />
<MESSAGE value="Better context and connection handling" /> <MESSAGE value="Better context and connection handling" />
<option name="LAST_COMMIT_MESSAGE" value="Better context and connection handling" /> <MESSAGE value="Lots of cleanups and refactoring" />
<option name="LAST_COMMIT_MESSAGE" value="Lots of cleanups and refactoring" />
</component> </component>
<component name="VgoProject"> <component name="VgoProject">
<integration-enabled>true</integration-enabled> <integration-enabled>true</integration-enabled>

View file

@ -16,6 +16,7 @@ mail and SMTP related tasks.
Some of the features of this library: Some of the features of this library:
* [X] Only Standard Library dependant * [X] Only Standard Library dependant
* [X] Modern, idiotmatic Go * [X] Modern, idiotmatic Go
* [X] Sane and secure defaults
* [X] SSL/TLS support * [X] SSL/TLS support
* [X] StartTLS support with different policies * [X] StartTLS support with different policies
* [X] Makes use of contexts for a better control flow and timeout/cancelation handling * [X] Makes use of contexts for a better control flow and timeout/cancelation handling

View file

@ -12,11 +12,17 @@ import (
"time" "time"
) )
// Defaults
const (
// DefaultPort is the default connection port cto the SMTP server // DefaultPort is the default connection port cto the SMTP server
const DefaultPort = 25 DefaultPort = 25
// DefaultTimeout is the default connection timeout // DefaultTimeout is the default connection timeout
const DefaultTimeout = time.Second * 30 DefaultTimeout = time.Second * 15
// DefaultTLSPolicy is the default STARTTLS policy
DefaultTLSPolicy = TLSMandatory
)
// Client is the SMTP client struct // Client is the SMTP client struct
type Client struct { type Client struct {
@ -84,8 +90,8 @@ func NewClient(h string, o ...Option) (*Client, error) {
cto: DefaultTimeout, cto: DefaultTimeout,
host: h, host: h,
port: DefaultPort, port: DefaultPort,
tlspolicy: TLSMandatory,
tlsconfig: &tls.Config{ServerName: h}, tlsconfig: &tls.Config{ServerName: h},
tlspolicy: DefaultTLSPolicy,
} }
// Set default HELO/EHLO hostname // Set default HELO/EHLO hostname
@ -257,28 +263,9 @@ func (c *Client) DialWithContext(pc context.Context) error {
return err return err
} }
if !c.ssl && c.tlspolicy != NoTLS { if err := c.tls(); err != nil {
est := false
st, _ := c.sc.Extension("STARTTLS")
if c.tlspolicy == TLSMandatory {
est = true
if !st {
return fmt.Errorf("STARTTLS mode set to: %q, but target host does not support STARTTLS",
c.tlspolicy)
}
}
if c.tlspolicy == TLSOpportunistic {
if st {
est = true
}
}
if est {
if err := c.sc.StartTLS(c.tlsconfig); err != nil {
return err return err
} }
}
_, c.enc = c.sc.TLSConnectionState()
}
if err := c.auth(); err != nil { if err := c.auth(); err != nil {
return err return err
@ -333,6 +320,33 @@ func (c *Client) checkConn() error {
return nil return nil
} }
// tls tries to make sure that the STARTTLS requirements are satisfied
func (c *Client) tls() error {
if !c.ssl && c.tlspolicy != NoTLS {
est := false
st, _ := c.sc.Extension("STARTTLS")
if c.tlspolicy == TLSMandatory {
est = true
if !st {
return fmt.Errorf("STARTTLS mode set to: %q, but target host does not support STARTTLS",
c.tlspolicy)
}
}
if c.tlspolicy == TLSOpportunistic {
if st {
est = true
}
}
if est {
if err := c.sc.StartTLS(c.tlsconfig); err != nil {
return err
}
}
_, c.enc = c.sc.TLSConnectionState()
}
return nil
}
// auth will try to perform SMTP AUTH if requested // auth will try to perform SMTP AUTH if requested
func (c *Client) auth() error { func (c *Client) auth() error {
if err := c.checkConn(); err != nil { if err := c.checkConn(); err != nil {

View file

@ -24,6 +24,7 @@ func main() {
m.ToIgnoreInvalid("test@test.de", "foo@bar.de", "blubb@blah.com") m.ToIgnoreInvalid("test@test.de", "foo@bar.de", "blubb@blah.com")
m.CcIgnoreInvalid("cc@test.de", "bar.de", "cc@blah.com") m.CcIgnoreInvalid("cc@test.de", "bar.de", "cc@blah.com")
m.BccIgnoreInvalid("bcc@test.de", "bcc@blah.com") m.BccIgnoreInvalid("bcc@test.de", "bcc@blah.com")
m.SetHeader("Foo", "bar")
m.SetMessageID() m.SetMessageID()
m.SetDate() m.SetDate()
m.SetBulk() m.SetBulk()

View file

@ -8,6 +8,12 @@ type AddrHeader string
// List of common generic header field names // List of common generic header field names
const ( const (
// HeaderContentLang is the "Content-Language" header
HeaderContentLang Header = "Content-Language"
// HeaderContentType is the "Content-Type" header
HeaderContentType Header = "Content-Type"
// HeaderDate represents the "Date" field // HeaderDate represents the "Date" field
// See: https://www.rfc-editor.org/rfc/rfc822#section-5.1 // See: https://www.rfc-editor.org/rfc/rfc822#section-5.1
HeaderDate Header = "Date" HeaderDate Header = "Date"
@ -16,6 +22,10 @@ const (
// See: https://www.rfc-editor.org/rfc/rfc1036#section-2.1.5 // See: https://www.rfc-editor.org/rfc/rfc1036#section-2.1.5
HeaderMessageID Header = "Message-ID" HeaderMessageID Header = "Message-ID"
// HeaderMIMEVersion represents the "MIME-Version" field as per RFC 2045
// See: https://datatracker.ietf.org/doc/html/rfc2045#section-4
HeaderMIMEVersion Header = "MIME-Version"
// HeaderPrecedence is the "Precedence" genHeader field // HeaderPrecedence is the "Precedence" genHeader field
HeaderPrecedence Header = "Precedence" HeaderPrecedence Header = "Precedence"