@@ -18,22 +18,24 @@ import (
18
18
19
19
// Email represents an email message.
20
20
type Email struct {
21
- from string
22
- sender string
23
- replyTo string
24
- returnPath string
25
- recipients []string
26
- headers textproto.MIMEHeader
27
- parts []part
28
- attachments []* File
29
- inlines []* File
30
- Charset string
31
- Encoding encoding
32
- Error error
33
- SMTPServer * smtpClient
34
- DkimMsg string
35
- AllowDuplicateAddress bool
36
- AddBccToHeader bool
21
+ from string
22
+ sender string
23
+ replyTo string
24
+ returnPath string
25
+ recipients []string
26
+ headers textproto.MIMEHeader
27
+ parts []part
28
+ attachments []* File
29
+ inlines []* File
30
+ Charset string
31
+ Encoding encoding
32
+ Error error
33
+ SMTPServer * smtpClient
34
+ DkimMsg string
35
+ AllowDuplicateAddress bool
36
+ AddBccToHeader bool
37
+ preserveOriginalRecipient bool
38
+ dsn []DSN
37
39
}
38
40
39
41
/*
@@ -59,10 +61,13 @@ type SMTPServer struct {
59
61
60
62
// SMTPClient represents a SMTP Client for send email
61
63
type SMTPClient struct {
62
- mu sync.Mutex
63
- Client * smtpClient
64
- KeepAlive bool
65
- SendTimeout time.Duration
64
+ mu sync.Mutex
65
+ Client * smtpClient
66
+ SendTimeout time.Duration
67
+ KeepAlive bool
68
+ hasDSNExt bool
69
+ preserveOriginalRecipient bool
70
+ dsn []DSN
66
71
}
67
72
68
73
// part represents the different content parts of an email body.
@@ -158,6 +163,34 @@ func (at AuthType) String() string {
158
163
}
159
164
}
160
165
166
+ /*
167
+ DSN notifications
168
+
169
+ - 'NEVER' under no circumstances a DSN must be returned to the sender. If you use NEVER all other notifications will be ignored.
170
+
171
+ - 'SUCCESS' will notify you when your mail has arrived at its destination.
172
+
173
+ - 'FAILURE' will arrive if an error occurred during delivery.
174
+
175
+ - 'DELAY' will notify you if there is an unusual delay in delivery, but the actual delivery's outcome (success or failure) is not yet decided.
176
+
177
+ see https://tools.ietf.org/html/rfc3461 See section 4.1 for more information about NOTIFY
178
+ */
179
+ type DSN int
180
+
181
+ const (
182
+ NEVER DSN = iota
183
+ FAILURE
184
+ DELAY
185
+ SUCCESS
186
+ )
187
+
188
+ var dsnTypes = [... ]string {"NEVER" , "FAILURE" , "DELAY" , "SUCCESS" }
189
+
190
+ func (dsn DSN ) String () string {
191
+ return dsnTypes [dsn ]
192
+ }
193
+
161
194
// NewMSG creates a new email. It uses UTF-8 by default. All charsets: http://webcheatsheet.com/HTML/character_sets_list.php
162
195
func NewMSG () * Email {
163
196
email := & Email {
@@ -613,6 +646,20 @@ func (email *Email) AddAlternativeData(contentType ContentType, body []byte) *Em
613
646
return email
614
647
}
615
648
649
+ // SetDSN sets the delivery status notification list, only is set when SMTP server supports DSN extension
650
+ //
651
+ // To preserve the original recipient of an email message, for example, if it is forwarded to another address, set preserveOriginalRecipient to true
652
+ func (email * Email ) SetDSN (dsn []DSN , preserveOriginalRecipient bool ) * Email {
653
+ if email .Error != nil {
654
+ return email
655
+ }
656
+
657
+ email .dsn = dsn
658
+ email .preserveOriginalRecipient = preserveOriginalRecipient
659
+
660
+ return email
661
+ }
662
+
616
663
// GetFrom returns the sender of the email, if any
617
664
func (email * Email ) GetFrom () string {
618
665
from := email .returnPath
@@ -710,6 +757,9 @@ func (email *Email) SendEnvelopeFrom(from string, client *SMTPClient) error {
710
757
msg = email .GetMessage ()
711
758
}
712
759
760
+ client .dsn = email .dsn
761
+ client .preserveOriginalRecipient = email .preserveOriginalRecipient
762
+
713
763
return send (from , email .recipients , msg , client )
714
764
}
715
765
@@ -864,10 +914,13 @@ func (server *SMTPServer) Connect() (*SMTPClient, error) {
864
914
}
865
915
}
866
916
917
+ _ , hasDSN := c .ext ["DSN" ]
918
+
867
919
return & SMTPClient {
868
920
Client : c ,
869
921
KeepAlive : server .KeepAlive ,
870
922
SendTimeout : server .SendTimeout ,
923
+ hasDSNExt : hasDSN ,
871
924
}, server .validateAuth (c )
872
925
}
873
926
@@ -965,9 +1018,31 @@ func sendMailProcess(from string, to []string, msg string, c *SMTPClient) error
965
1018
return err
966
1019
}
967
1020
1021
+ var dsn string
1022
+ var dsnSet bool
1023
+
1024
+ if c .hasDSNExt && len (c .dsn ) > 0 {
1025
+ dsn = " NOTIFY="
1026
+ if hasNeverDSN (c .dsn ) {
1027
+ dsn += NEVER .String ()
1028
+ } else {
1029
+ dsn += strings .Join (dsnToString (c .dsn ), "," )
1030
+ }
1031
+
1032
+ if c .preserveOriginalRecipient {
1033
+ dsn += " ORCPT=rfc822;"
1034
+ }
1035
+
1036
+ dsnSet = true
1037
+ }
1038
+
968
1039
// Set the recipients
969
1040
for _ , address := range to {
970
- if err := c .Client .rcpt (address ); err != nil {
1041
+ if dsnSet && c .preserveOriginalRecipient {
1042
+ dsn += address
1043
+ }
1044
+
1045
+ if err := c .Client .rcpt (address , dsn ); err != nil {
971
1046
return err
972
1047
}
973
1048
}
@@ -1001,3 +1076,20 @@ func checkKeepAlive(client *SMTPClient) {
1001
1076
client .Close ()
1002
1077
}
1003
1078
}
1079
+
1080
+ func hasNeverDSN (dsnList []DSN ) bool {
1081
+ for i := range dsnList {
1082
+ if dsnList [i ] == NEVER {
1083
+ return true
1084
+ }
1085
+ }
1086
+ return false
1087
+ }
1088
+
1089
+ func dsnToString (dsnList []DSN ) []string {
1090
+ dsnString := make ([]string , len (dsnList ))
1091
+ for i := range dsnList {
1092
+ dsnString [i ] = dsnList [i ].String ()
1093
+ }
1094
+ return dsnString
1095
+ }
0 commit comments