@@ -44,6 +44,29 @@ type Client struct {
4444 DebugWriter io.Writer
4545}
4646
47+ // Options used when creating an SMTP client. If a field is left to its zero
48+ // value, the default will be used instead.
49+ type ClientOptions struct {
50+ // the name to use in HELO/EHLO/LHLO (default: "localhost")
51+ LocalName string
52+ // Time to wait for command responses (this includes 3xx reply to DATA)
53+ // (default: 5 minutes)
54+ CommandTimeout time.Duration
55+ // Time to wait for responses after final dot (default 12 minutes).
56+ SubmissionTimeout time.Duration
57+ }
58+
59+ const (
60+ DefaultLocalName = "localhost"
61+ // As recommended by RFC 5321. For DATA command reply (3xx one) RFC
62+ // recommends a slightly shorter timeout but we do not bother
63+ // differentiating these.
64+ DefaultCommandTimeout = 5 * time .Minute
65+ // 10 minutes + 2 minute buffer in case the server is doing transparent
66+ // forwarding and also follows recommended timeouts.
67+ DefaultSubmissionTimeout = 12 * time .Minute
68+ )
69+
4770// 30 seconds was chosen as it's the same duration as http.DefaultTransport's
4871// timeout.
4972var defaultDialer = net.Dialer {Timeout : 30 * time .Second }
@@ -54,11 +77,17 @@ var defaultDialer = net.Dialer{Timeout: 30 * time.Second}
5477// This function returns a plaintext connection. To enable TLS, use
5578// DialStartTLS.
5679func Dial (addr string ) (* Client , error ) {
80+ return DialWithOptions (addr , nil )
81+ }
82+
83+ // DialWithOpts returns a new Client connected to an SMTP server at addr using
84+ // custom options instead of the defaults.
85+ func DialWithOptions (addr string , opts * ClientOptions ) (* Client , error ) {
5786 conn , err := defaultDialer .Dial ("tcp" , addr )
5887 if err != nil {
5988 return nil , err
6089 }
61- client := NewClient (conn )
90+ client := NewClientWithOptions (conn , opts )
6291 client .serverName , _ , _ = net .SplitHostPort (addr )
6392 return client , nil
6493}
@@ -68,6 +97,14 @@ func Dial(addr string) (*Client, error) {
6897//
6998// A nil tlsConfig is equivalent to a zero tls.Config.
7099func DialTLS (addr string , tlsConfig * tls.Config ) (* Client , error ) {
100+ return DialTLSWithOptions (addr , tlsConfig , nil )
101+ }
102+
103+ // DialTLSWithOpts returns a new Client connected to an SMTP server via TLS at
104+ // addr using custom options instead of the defaults.
105+ func DialTLSWithOptions (
106+ addr string , tlsConfig * tls.Config , opts * ClientOptions ,
107+ ) (* Client , error ) {
71108 tlsDialer := tls.Dialer {
72109 NetDialer : & defaultDialer ,
73110 Config : tlsConfig ,
@@ -76,7 +113,7 @@ func DialTLS(addr string, tlsConfig *tls.Config) (*Client, error) {
76113 if err != nil {
77114 return nil , err
78115 }
79- client := NewClient (conn )
116+ client := NewClientWithOptions (conn , opts )
80117 client .serverName , _ , _ = net .SplitHostPort (addr )
81118 return client , nil
82119}
@@ -86,7 +123,15 @@ func DialTLS(addr string, tlsConfig *tls.Config) (*Client, error) {
86123//
87124// A nil tlsConfig is equivalent to a zero tls.Config.
88125func DialStartTLS (addr string , tlsConfig * tls.Config ) (* Client , error ) {
89- c , err := Dial (addr )
126+ return DialStartTLSWithOptions (addr , tlsConfig , nil )
127+ }
128+
129+ // DialStartTLSWithOpts retruns a new Client connected to an SMTP server via
130+ // STARTTLS at addr using custom options instead of the defaults.
131+ func DialStartTLSWithOptions (
132+ addr string , tlsConfig * tls.Config , opts * ClientOptions ,
133+ ) (* Client , error ) {
134+ c , err := DialWithOptions (addr , opts )
90135 if err != nil {
91136 return nil , err
92137 }
@@ -100,25 +145,45 @@ func DialStartTLS(addr string, tlsConfig *tls.Config) (*Client, error) {
100145// NewClient returns a new Client using an existing connection and host as a
101146// server name to be used when authenticating.
102147func NewClient (conn net.Conn ) * Client {
148+ return NewClientWithOptions (conn , nil )
149+ }
150+
151+ // NewClient returns a new Client using an existing connection using custom
152+ // options instead of the defaults.
153+ func NewClientWithOptions (conn net.Conn , opts * ClientOptions ) * Client {
154+ if opts == nil {
155+ opts = new (ClientOptions )
156+ }
103157 c := & Client {
104- localName : "localhost" ,
105- // As recommended by RFC 5321. For DATA command reply (3xx one) RFC
106- // recommends a slightly shorter timeout but we do not bother
107- // differentiating these.
108- CommandTimeout : 5 * time .Minute ,
109- // 10 minutes + 2 minute buffer in case the server is doing transparent
110- // forwarding and also follows recommended timeouts.
111- SubmissionTimeout : 12 * time .Minute ,
158+ localName : opts .LocalName ,
159+ CommandTimeout : opts .CommandTimeout ,
160+ SubmissionTimeout : opts .SubmissionTimeout ,
161+ }
162+ if c .localName == "" {
163+ c .localName = DefaultLocalName
164+ }
165+ if c .CommandTimeout == 0 {
166+ c .CommandTimeout = DefaultCommandTimeout
167+ }
168+ if c .SubmissionTimeout == 0 {
169+ c .SubmissionTimeout = DefaultSubmissionTimeout
112170 }
113-
114171 c .setConn (conn )
115172
116173 return c
117174}
118175
119176// NewClientStartTLS creates a new Client and performs a STARTTLS command.
120177func NewClientStartTLS (conn net.Conn , tlsConfig * tls.Config ) (* Client , error ) {
121- c := NewClient (conn )
178+ return NewClientStartTLSWithOptions (conn , tlsConfig , nil )
179+ }
180+
181+ // NewClientStartTLS creates a new Client and performs a STARTTLS command.
182+ // It allows using custom options instead of the defaults.
183+ func NewClientStartTLSWithOptions (
184+ conn net.Conn , tlsConfig * tls.Config , opts * ClientOptions ,
185+ ) (* Client , error ) {
186+ c := NewClientWithOptions (conn , opts )
122187 if err := initStartTLS (c , tlsConfig ); err != nil {
123188 c .Close ()
124189 return nil , err
@@ -142,7 +207,13 @@ func initStartTLS(c *Client, tlsConfig *tls.Config) error {
142207// NewClientLMTP returns a new LMTP Client (as defined in RFC 2033) using an
143208// existing connection and host as a server name to be used when authenticating.
144209func NewClientLMTP (conn net.Conn ) * Client {
145- c := NewClient (conn )
210+ return NewClientLMTPWithOptions (conn , nil )
211+ }
212+
213+ // NewClientLMTP returns a new LMTP Client (as defined in RFC 2033) using
214+ // custom options instead of the defaults.
215+ func NewClientLMTPWithOptions (conn net.Conn , opts * ClientOptions ) * Client {
216+ c := NewClientWithOptions (conn , opts )
146217 c .lmtp = true
147218 return c
148219}
0 commit comments