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" />
</component>
<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$/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$/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$/mailmsg.go" beforeDir="false" afterPath="$PROJECT_DIR$/mailmsg.go" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -97,7 +96,8 @@
<MESSAGE value="Progress" />
<MESSAGE value="Implemented SMTP AUTH" />
<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 name="VgoProject">
<integration-enabled>true</integration-enabled>

View file

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

View file

@ -12,11 +12,17 @@ import (
"time"
)
// DefaultPort is the default connection port cto the SMTP server
const DefaultPort = 25
// Defaults
const (
// DefaultPort is the default connection port cto the SMTP server
DefaultPort = 25
// DefaultTimeout is the default connection timeout
const DefaultTimeout = time.Second * 30
// DefaultTimeout is the default connection timeout
DefaultTimeout = time.Second * 15
// DefaultTLSPolicy is the default STARTTLS policy
DefaultTLSPolicy = TLSMandatory
)
// Client is the SMTP client struct
type Client struct {
@ -84,8 +90,8 @@ func NewClient(h string, o ...Option) (*Client, error) {
cto: DefaultTimeout,
host: h,
port: DefaultPort,
tlspolicy: TLSMandatory,
tlsconfig: &tls.Config{ServerName: h},
tlspolicy: DefaultTLSPolicy,
}
// Set default HELO/EHLO hostname
@ -257,27 +263,8 @@ func (c *Client) DialWithContext(pc context.Context) error {
return err
}
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()
if err := c.tls(); err != nil {
return err
}
if err := c.auth(); err != nil {
@ -333,6 +320,33 @@ func (c *Client) checkConn() error {
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
func (c *Client) auth() error {
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.CcIgnoreInvalid("cc@test.de", "bar.de", "cc@blah.com")
m.BccIgnoreInvalid("bcc@test.de", "bcc@blah.com")
m.SetHeader("Foo", "bar")
m.SetMessageID()
m.SetDate()
m.SetBulk()

View file

@ -8,6 +8,12 @@ type AddrHeader string
// List of common generic header field names
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
// See: https://www.rfc-editor.org/rfc/rfc822#section-5.1
HeaderDate Header = "Date"
@ -16,6 +22,10 @@ const (
// See: https://www.rfc-editor.org/rfc/rfc1036#section-2.1.5
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 Header = "Precedence"