envelope.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. package mail
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "encoding/hex"
  6. "errors"
  7. "fmt"
  8. "hash"
  9. "hash/fnv"
  10. "io"
  11. "mime"
  12. "net"
  13. "net/textproto"
  14. "strings"
  15. "sync"
  16. "time"
  17. "github.com/flashmob/go-guerrilla/mail/mimeparse"
  18. "github.com/flashmob/go-guerrilla/mail/smtp"
  19. )
  20. // A WordDecoder decodes MIME headers containing RFC 2047 encoded-words.
  21. // Used by the MimeHeaderDecode function.
  22. // It's exposed public so that an alternative decoder can be set, eg Gnu iconv
  23. // by importing the mail/inconv package.
  24. // Another alternative would be to use https://godoc.org/golang.org/x/text/encoding
  25. var Dec mime.WordDecoder
  26. func init() {
  27. // use the default decoder, without Gnu inconv. Import the mail/inconv package to use iconv.
  28. Dec = mime.WordDecoder{}
  29. // for QueuedID generation
  30. hasher.h = fnv.New128a()
  31. }
  32. // Address encodes an email address of the form `<user@host>`
  33. type Address struct {
  34. // User is local part
  35. User string
  36. // Host is the domain
  37. Host string
  38. // ADL is at-domain list if matched
  39. ADL []string
  40. // PathParams contains any ESTMP parameters that were matched
  41. PathParams []smtp.PathParam
  42. // NullPath is true if <> was received
  43. NullPath bool
  44. // Quoted indicates if the local-part needs quotes
  45. Quoted bool
  46. // IP stores the IP Address, if the Host is an IP
  47. IP net.IP
  48. // DisplayName is a label before the address (RFC5322)
  49. DisplayName string
  50. // DisplayNameQuoted is true when DisplayName was quoted
  51. DisplayNameQuoted bool
  52. }
  53. func (a *Address) String() string {
  54. var local string
  55. if a.IsEmpty() {
  56. return ""
  57. }
  58. if a.User == "postmaster" && a.Host == "" {
  59. return "postmaster"
  60. }
  61. if a.Quoted {
  62. var sb bytes.Buffer
  63. sb.WriteByte('"')
  64. for i := 0; i < len(a.User); i++ {
  65. if a.User[i] == '\\' || a.User[i] == '"' {
  66. // escape
  67. sb.WriteByte('\\')
  68. }
  69. sb.WriteByte(a.User[i])
  70. }
  71. sb.WriteByte('"')
  72. local = sb.String()
  73. } else {
  74. local = a.User
  75. }
  76. if a.Host != "" {
  77. if a.IP != nil {
  78. return fmt.Sprintf("%s@[%s]", local, a.Host)
  79. }
  80. return fmt.Sprintf("%s@%s", local, a.Host)
  81. }
  82. return local
  83. }
  84. func (a *Address) IsEmpty() bool {
  85. return a.User == "" && a.Host == ""
  86. }
  87. func (a *Address) IsPostmaster() bool {
  88. if a.User == "postmaster" {
  89. return true
  90. }
  91. return false
  92. }
  93. // NewAddress takes a string of an RFC 5322 address of the
  94. // form "Gogh Fir <[email protected]>" or "[email protected]".
  95. func NewAddress(str string) (*Address, error) {
  96. var ap smtp.RFC5322
  97. l, err := ap.Address([]byte(str))
  98. if err != nil {
  99. return nil, err
  100. }
  101. if len(l.List) == 0 {
  102. return nil, errors.New("no email address matched")
  103. }
  104. a := new(Address)
  105. addr := &l.List[0]
  106. a.User = addr.LocalPart
  107. a.Quoted = addr.LocalPartQuoted
  108. a.Host = addr.Domain
  109. a.IP = addr.IP
  110. a.DisplayName = addr.DisplayName
  111. a.DisplayNameQuoted = addr.DisplayNameQuoted
  112. a.NullPath = addr.NullPath
  113. return a, nil
  114. }
  115. type Hash128 [16]byte
  116. func (h Hash128) String() string {
  117. return fmt.Sprintf("%x", h[:])
  118. }
  119. // FromHex converts the, string must be 32 bytes
  120. func (h *Hash128) FromHex(s string) {
  121. if len(s) != 32 {
  122. panic("hex string must be 32 bytes")
  123. }
  124. _, _ = hex.Decode(h[:], []byte(s))
  125. }
  126. // Bytes returns the raw bytes
  127. func (h Hash128) Bytes() []byte { return h[:] }
  128. // Envelope of Email represents a single SMTP message.
  129. type Envelope struct {
  130. // Data stores the header and message body (when using the non-streaming processor)
  131. Data bytes.Buffer
  132. // Subject stores the subject of the email, extracted and decoded after calling ParseHeaders()
  133. Subject string
  134. // Header stores the results from ParseHeaders()
  135. Header textproto.MIMEHeader
  136. // Values hold the values generated when processing the envelope by the backend
  137. Values map[string]interface{}
  138. // Hashes of each email on the rcpt
  139. Hashes []string
  140. // DeliveryHeader stores additional delivery header that may be added (used by non-streaming processor)
  141. DeliveryHeader string
  142. // Size is the length of message, after being written
  143. Size int64
  144. // MimeParts contain the information about the mime-parts after they have been parsed
  145. MimeParts *mimeparse.Parts
  146. // MimeError contains any error encountered when parsing mime using the mimeanalyzer
  147. MimeError error
  148. // MessageID contains the id of the message after it has been written
  149. MessageID uint64
  150. // Remote IP address
  151. RemoteIP string
  152. // Message sent in EHLO command
  153. Helo string
  154. // Sender
  155. MailFrom Address
  156. // Recipients
  157. RcptTo []Address
  158. // TLS is true if the email was received using a TLS connection
  159. TLS bool
  160. // Email(s) will be queued with this id
  161. QueuedId Hash128
  162. // TransportType indicates whenever 8BITMIME extension has been signaled
  163. TransportType smtp.TransportType
  164. // ESMTP: true if EHLO was used
  165. ESMTP bool
  166. // ServerID records the server's index in the configuration
  167. ServerID int
  168. // When locked, it means that the envelope is being processed by the backend
  169. sync.WaitGroup
  170. }
  171. type queuedIDGenerator struct {
  172. h hash.Hash
  173. n [24]byte
  174. sync.Mutex
  175. }
  176. var hasher queuedIDGenerator
  177. func NewEnvelope(remoteAddr string, clientID uint64, serverID int) *Envelope {
  178. return &Envelope{
  179. RemoteIP: remoteAddr,
  180. Values: make(map[string]interface{}),
  181. ServerID: serverID,
  182. QueuedId: QueuedID(clientID, serverID),
  183. }
  184. }
  185. func QueuedID(clientID uint64, serverID int) Hash128 {
  186. hasher.Lock()
  187. defer func() {
  188. hasher.h.Reset()
  189. hasher.Unlock()
  190. }()
  191. h := Hash128{}
  192. // pack the seeds and hash'em
  193. binary.BigEndian.PutUint64(hasher.n[0:8], uint64(time.Now().UnixNano()))
  194. binary.BigEndian.PutUint64(hasher.n[8:16], clientID)
  195. binary.BigEndian.PutUint64(hasher.n[16:24], uint64(serverID))
  196. hasher.h.Write(hasher.n[:])
  197. copy(h[:], hasher.h.Sum([]byte{}))
  198. return h
  199. }
  200. // ParseHeaders parses the headers into Header field of the Envelope struct.
  201. // Data buffer must be full before calling.
  202. // It assumes that at most 30kb of email data can be a header
  203. // Decoding of encoding to UTF is only done on the Subject, where the result is assigned to the Subject field
  204. func (e *Envelope) ParseHeaders() error {
  205. if e.Header == nil {
  206. return errors.New("headers not parsed")
  207. }
  208. if len(e.Header) == 0 {
  209. return errors.New("header not found")
  210. }
  211. // decode the subject
  212. if subject, ok := e.Header["Subject"]; ok {
  213. e.Subject = MimeHeaderDecode(subject[0])
  214. }
  215. return nil
  216. }
  217. // Len returns the number of bytes that would be in the reader returned by NewReader()
  218. func (e *Envelope) Len() int {
  219. return len(e.DeliveryHeader) + e.Data.Len()
  220. }
  221. // NewReader returns a new reader for reading the email contents, including the delivery headers
  222. func (e *Envelope) NewReader() io.Reader {
  223. return io.MultiReader(
  224. strings.NewReader(e.DeliveryHeader),
  225. bytes.NewReader(e.Data.Bytes()),
  226. )
  227. }
  228. // String converts the email to string.
  229. // Typically, you would want to use the compressor guerrilla.Processor for more efficiency, or use NewReader
  230. func (e *Envelope) String() string {
  231. return e.DeliveryHeader + e.Data.String()
  232. }
  233. // ResetTransaction is called when the transaction is reset (keeping the connection open)
  234. func (e *Envelope) ResetTransaction() {
  235. // ensure not processing by the backend, will only get lock if finished, otherwise block
  236. e.Wait()
  237. e.MailFrom = Address{}
  238. e.RcptTo = []Address{}
  239. // reset the data buffer, keep it allocated
  240. e.Data.Reset()
  241. // todo: these are probably good candidates for buffers / use sync.Pool (after profiling)
  242. e.Subject = ""
  243. e.Header = nil
  244. e.Hashes = make([]string, 0)
  245. e.DeliveryHeader = ""
  246. e.Size = 0
  247. e.MessageID = 0
  248. e.MimeParts = nil
  249. e.MimeError = nil
  250. e.Values = make(map[string]interface{})
  251. }
  252. // Reseed is called when used with a new connection, once it's accepted
  253. func (e *Envelope) Reseed(remoteIP string, clientID uint64, serverID int) {
  254. e.RemoteIP = remoteIP
  255. e.ServerID = serverID
  256. e.QueuedId = QueuedID(clientID, serverID)
  257. e.Helo = ""
  258. e.TLS = false
  259. e.ESMTP = false
  260. }
  261. // PushRcpt adds a recipient email address to the envelope
  262. func (e *Envelope) PushRcpt(addr Address) {
  263. e.RcptTo = append(e.RcptTo, addr)
  264. }
  265. // PopRcpt removes the last email address that was pushed to the envelope
  266. func (e *Envelope) PopRcpt() Address {
  267. ret := e.RcptTo[len(e.RcptTo)-1]
  268. e.RcptTo = e.RcptTo[:len(e.RcptTo)-1]
  269. return ret
  270. }
  271. func (e *Envelope) Protocol() Protocol {
  272. protocol := ProtocolSMTP
  273. switch {
  274. case !e.ESMTP && !e.TLS:
  275. protocol = ProtocolSMTP
  276. case !e.ESMTP && e.TLS:
  277. protocol = ProtocolSMTPS
  278. case e.ESMTP && !e.TLS:
  279. protocol = ProtocolESMTP
  280. case e.ESMTP && e.TLS:
  281. protocol = ProtocolESMTPS
  282. }
  283. return protocol
  284. }
  285. type Protocol int
  286. const (
  287. ProtocolSMTP Protocol = iota
  288. ProtocolSMTPS
  289. ProtocolESMTP
  290. ProtocolESMTPS
  291. ProtocolLTPS
  292. ProtocolUnknown
  293. )
  294. func (p Protocol) String() string {
  295. switch p {
  296. case ProtocolSMTP:
  297. return "SMTP"
  298. case ProtocolSMTPS:
  299. return "SMTPS"
  300. case ProtocolESMTP:
  301. return "ESMTP"
  302. case ProtocolESMTPS:
  303. return "ESMTPS"
  304. case ProtocolLTPS:
  305. return "LTPS"
  306. }
  307. return "unknown"
  308. }
  309. func ParseProtocolType(str string) Protocol {
  310. switch {
  311. case str == "SMTP":
  312. return ProtocolSMTP
  313. case str == "SMTPS":
  314. return ProtocolSMTPS
  315. case str == "ESMTP":
  316. return ProtocolESMTP
  317. case str == "ESMTPS":
  318. return ProtocolESMTPS
  319. case str == "LTPS":
  320. return ProtocolLTPS
  321. }
  322. return ProtocolUnknown
  323. }
  324. const (
  325. statePlainText = iota
  326. stateStartEncodedWord
  327. stateEncoding
  328. stateCharset
  329. statePayload
  330. statePayloadEnd
  331. )
  332. // MimeHeaderDecode converts 7 bit encoded mime header strings to UTF-8
  333. func MimeHeaderDecode(str string) string {
  334. // optimized to only create an output buffer if there's need to
  335. // the `out` buffer is only made if an encoded word was decoded without error
  336. // `out` is made with the capacity of len(str)
  337. // a simple state machine is used to detect the start & end of encoded word and plain-text
  338. state := statePlainText
  339. var (
  340. out []byte
  341. wordStart int // start of an encoded word
  342. wordLen int // end of an encoded
  343. ptextStart = -1 // start of plan-text
  344. ptextLen int // end of plain-text
  345. )
  346. for i := 0; i < len(str); i++ {
  347. switch state {
  348. case statePlainText:
  349. if ptextStart == -1 {
  350. ptextStart = i
  351. }
  352. if str[i] == '=' {
  353. state = stateStartEncodedWord
  354. wordStart = i
  355. wordLen = 1
  356. } else {
  357. ptextLen++
  358. }
  359. case stateStartEncodedWord:
  360. if str[i] == '?' {
  361. wordLen++
  362. state = stateCharset
  363. } else {
  364. wordLen = 0
  365. state = statePlainText
  366. ptextLen++
  367. }
  368. case stateCharset:
  369. if str[i] == '?' {
  370. wordLen++
  371. state = stateEncoding
  372. } else if str[i] >= 'a' && str[i] <= 'z' ||
  373. str[i] >= 'A' && str[i] <= 'Z' ||
  374. str[i] >= '0' && str[i] <= '9' || str[i] == '-' {
  375. wordLen++
  376. } else {
  377. // error
  378. state = statePlainText
  379. ptextLen += wordLen
  380. wordLen = 0
  381. }
  382. case stateEncoding:
  383. if str[i] == '?' {
  384. wordLen++
  385. state = statePayload
  386. } else if str[i] == 'Q' || str[i] == 'q' || str[i] == 'b' || str[i] == 'B' {
  387. wordLen++
  388. } else {
  389. // abort
  390. state = statePlainText
  391. ptextLen += wordLen
  392. wordLen = 0
  393. }
  394. case statePayload:
  395. if str[i] == '?' {
  396. wordLen++
  397. state = statePayloadEnd
  398. } else {
  399. wordLen++
  400. }
  401. case statePayloadEnd:
  402. if str[i] == '=' {
  403. wordLen++
  404. var err error
  405. out, err = decodeWordAppend(ptextLen, out, str, ptextStart, wordStart, wordLen)
  406. if err != nil && out == nil {
  407. // special case: there was an error with decoding and `out` wasn't created
  408. // we can assume the encoded word as plaintext
  409. ptextLen += wordLen //+ 1 // add 1 for the space/tab
  410. wordLen = 0
  411. wordStart = 0
  412. state = statePlainText
  413. continue
  414. }
  415. if skip := hasEncodedWordAhead(str, i+1); skip != -1 {
  416. i = skip
  417. } else {
  418. out = makeAppend(out, len(str), []byte{})
  419. }
  420. ptextStart = -1
  421. ptextLen = 0
  422. wordLen = 0
  423. wordStart = 0
  424. state = statePlainText
  425. } else {
  426. // abort
  427. state = statePlainText
  428. ptextLen += wordLen
  429. wordLen = 0
  430. }
  431. }
  432. }
  433. if out != nil && ptextLen > 0 {
  434. out = makeAppend(out, len(str), []byte(str[ptextStart:ptextStart+ptextLen]))
  435. ptextLen = 0
  436. }
  437. if out == nil {
  438. // best case: there was nothing to encode
  439. return str
  440. }
  441. return string(out)
  442. }
  443. func decodeWordAppend(ptextLen int, out []byte, str string, ptextStart int, wordStart int, wordLen int) ([]byte, error) {
  444. if ptextLen > 0 {
  445. out = makeAppend(out, len(str), []byte(str[ptextStart:ptextStart+ptextLen]))
  446. }
  447. d, err := Dec.Decode(str[wordStart : wordLen+wordStart])
  448. if err == nil {
  449. out = makeAppend(out, len(str), []byte(d))
  450. } else if out != nil {
  451. out = makeAppend(out, len(str), []byte(str[wordStart:wordLen+wordStart]))
  452. }
  453. return out, err
  454. }
  455. func makeAppend(out []byte, size int, in []byte) []byte {
  456. if out == nil {
  457. out = make([]byte, 0, size)
  458. }
  459. out = append(out, in...)
  460. return out
  461. }
  462. func hasEncodedWordAhead(str string, i int) int {
  463. for ; i+2 < len(str); i++ {
  464. if str[i] != ' ' && str[i] != '\t' {
  465. return -1
  466. }
  467. if str[i+1] == '=' && str[i+2] == '?' {
  468. return i
  469. }
  470. }
  471. return -1
  472. }
  473. // Envelopes have their own pool
  474. type Pool struct {
  475. // envelopes that are ready to be borrowed
  476. pool chan *Envelope
  477. // semaphore to control number of maximum borrowed envelopes
  478. sem chan bool
  479. }
  480. func NewPool(poolSize int) *Pool {
  481. return &Pool{
  482. pool: make(chan *Envelope, poolSize),
  483. sem: make(chan bool, poolSize),
  484. }
  485. }
  486. func (p *Pool) Borrow(remoteAddr string, clientID uint64, serverID int) *Envelope {
  487. var e *Envelope
  488. p.sem <- true // block the envelope until more room
  489. select {
  490. case e = <-p.pool:
  491. e.Reseed(remoteAddr, clientID, serverID)
  492. default:
  493. e = NewEnvelope(remoteAddr, clientID, serverID)
  494. }
  495. return e
  496. }
  497. // Return returns an envelope back to the envelope pool
  498. // Make sure that envelope finished processing before calling this
  499. func (p *Pool) Return(e *Envelope) {
  500. select {
  501. case p.pool <- e:
  502. //placed envelope back in pool
  503. default:
  504. // pool is full, discard it
  505. }
  506. // take a value off the semaphore to make room for more envelopes
  507. <-p.sem
  508. }
  509. const MostCommonCharset = "ISO-8859-1"
  510. var supportedEncodingsCharsets map[string]bool
  511. func SupportsCharset(charset string) bool {
  512. if supportedEncodingsCharsets == nil {
  513. supportedEncodingsCharsets = make(map[string]bool)
  514. } else if ok, result := supportedEncodingsCharsets[charset]; ok {
  515. return result
  516. }
  517. _, err := Dec.CharsetReader(charset, bytes.NewReader([]byte{}))
  518. if err != nil {
  519. supportedEncodingsCharsets[charset] = false
  520. return false
  521. }
  522. supportedEncodingsCharsets[charset] = true
  523. return true
  524. }