envelope.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. package mail
  2. import (
  3. "bufio"
  4. "bytes"
  5. "crypto/md5"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "mime"
  10. "net"
  11. "net/textproto"
  12. "strings"
  13. "sync"
  14. "time"
  15. "github.com/flashmob/go-guerrilla/mail/rfc5321"
  16. )
  17. // A WordDecoder decodes MIME headers containing RFC 2047 encoded-words.
  18. // Used by the MimeHeaderDecode function.
  19. // It's exposed public so that an alternative decoder can be set, eg Gnu iconv
  20. // by importing the mail/inconv package.
  21. // Another alternative would be to use https://godoc.org/golang.org/x/text/encoding
  22. var Dec mime.WordDecoder
  23. func init() {
  24. // use the default decoder, without Gnu inconv. Import the mail/inconv package to use iconv.
  25. Dec = mime.WordDecoder{}
  26. }
  27. const maxHeaderChunk = 1 + (4 << 10) // 4KB
  28. // Address encodes an email address of the form `<user@host>`
  29. type Address struct {
  30. // User is local part
  31. User string
  32. // Host is the domain
  33. Host string
  34. // ADL is at-domain list if matched
  35. ADL []string
  36. // PathParams contains any ESTMP parameters that were matched
  37. PathParams [][]string
  38. // NullPath is true if <> was received
  39. NullPath bool
  40. // Quoted indicates if the local-part needs quotes
  41. Quoted bool
  42. // IP stores the IP Address, if the Host is an IP
  43. IP net.IP
  44. // DisplayName is a label before the address (RFC5322)
  45. DisplayName string
  46. // DisplayNameQuoted is true when DisplayName was quoted
  47. DisplayNameQuoted bool
  48. }
  49. func (a *Address) String() string {
  50. var local string
  51. if a.IsEmpty() {
  52. return ""
  53. }
  54. if a.User == "postmaster" && a.Host == "" {
  55. return "postmaster"
  56. }
  57. if a.Quoted {
  58. var sb bytes.Buffer
  59. sb.WriteByte('"')
  60. for i := 0; i < len(a.User); i++ {
  61. if a.User[i] == '\\' || a.User[i] == '"' {
  62. // escape
  63. sb.WriteByte('\\')
  64. }
  65. sb.WriteByte(a.User[i])
  66. }
  67. sb.WriteByte('"')
  68. local = sb.String()
  69. } else {
  70. local = a.User
  71. }
  72. if a.Host != "" {
  73. if a.IP != nil {
  74. return fmt.Sprintf("%s@[%s]", local, a.Host)
  75. }
  76. return fmt.Sprintf("%s@%s", local, a.Host)
  77. }
  78. return local
  79. }
  80. func (a *Address) IsEmpty() bool {
  81. return a.User == "" && a.Host == ""
  82. }
  83. func (a *Address) IsPostmaster() bool {
  84. if a.User == "postmaster" {
  85. return true
  86. }
  87. return false
  88. }
  89. // NewAddress takes a string of an RFC 5322 address of the
  90. // form "Gogh Fir <[email protected]>" or "[email protected]".
  91. func NewAddress(str string) (*Address, error) {
  92. var ap rfc5321.RFC5322
  93. l, err := ap.Address([]byte(str))
  94. if err != nil {
  95. return nil, err
  96. }
  97. if len(l.List) == 0 {
  98. return nil, errors.New("no email address matched")
  99. }
  100. a := new(Address)
  101. addr := &l.List[0]
  102. a.User = addr.LocalPart
  103. a.Quoted = addr.LocalPartQuoted
  104. a.Host = addr.Domain
  105. a.IP = addr.IP
  106. a.DisplayName = addr.DisplayName
  107. a.DisplayNameQuoted = addr.DisplayNameQuoted
  108. a.NullPath = addr.NullPath
  109. return a, nil
  110. }
  111. // Envelope of Email represents a single SMTP message.
  112. type Envelope struct {
  113. // Remote IP address
  114. RemoteIP string
  115. // Message sent in EHLO command
  116. Helo string
  117. // Sender
  118. MailFrom Address
  119. // Recipients
  120. RcptTo []Address
  121. // Data stores the header and message body
  122. Data bytes.Buffer
  123. // Subject stores the subject of the email, extracted and decoded after calling ParseHeaders()
  124. Subject string
  125. // TLS is true if the email was received using a TLS connection
  126. TLS bool
  127. // Header stores the results from ParseHeaders()
  128. Header textproto.MIMEHeader
  129. // Values hold the values generated when processing the envelope by the backend
  130. Values map[string]interface{}
  131. // Hashes of each email on the rcpt
  132. Hashes []string
  133. // additional delivery header that may be added
  134. DeliveryHeader string
  135. // Email(s) will be queued with this id
  136. QueuedId string
  137. // ESMTP: true if EHLO was used
  138. ESMTP bool
  139. // When locked, it means that the envelope is being processed by the backend
  140. sync.Mutex
  141. }
  142. func NewEnvelope(remoteAddr string, clientID uint64) *Envelope {
  143. return &Envelope{
  144. RemoteIP: remoteAddr,
  145. Values: make(map[string]interface{}),
  146. QueuedId: queuedID(clientID),
  147. }
  148. }
  149. func queuedID(clientID uint64) string {
  150. return fmt.Sprintf("%x", md5.Sum([]byte(string(time.Now().Unix())+string(clientID))))
  151. }
  152. // ParseHeaders parses the headers into Header field of the Envelope struct.
  153. // Data buffer must be full before calling.
  154. // It assumes that at most 30kb of email data can be a header
  155. // Decoding of encoding to UTF is only done on the Subject, where the result is assigned to the Subject field
  156. func (e *Envelope) ParseHeaders() error {
  157. var err error
  158. if e.Header != nil {
  159. return errors.New("headers already parsed")
  160. }
  161. buf := e.Data.Bytes()
  162. // find where the header ends, assuming that over 30 kb would be max
  163. if len(buf) > maxHeaderChunk {
  164. buf = buf[:maxHeaderChunk]
  165. }
  166. headerEnd := bytes.Index(buf, []byte{'\n', '\n'}) // the first two new-lines chars are the End Of Header
  167. if headerEnd > -1 {
  168. header := buf[0 : headerEnd+2]
  169. headerReader := textproto.NewReader(bufio.NewReader(bytes.NewBuffer(header)))
  170. e.Header, err = headerReader.ReadMIMEHeader()
  171. if err == nil || err == io.EOF {
  172. // decode the subject
  173. if subject, ok := e.Header["Subject"]; ok {
  174. e.Subject = MimeHeaderDecode(subject[0])
  175. }
  176. }
  177. } else {
  178. err = errors.New("header not found")
  179. }
  180. return err
  181. }
  182. // Len returns the number of bytes that would be in the reader returned by NewReader()
  183. func (e *Envelope) Len() int {
  184. return len(e.DeliveryHeader) + e.Data.Len()
  185. }
  186. // NewReader returns a new reader for reading the email contents, including the delivery headers
  187. func (e *Envelope) NewReader() io.Reader {
  188. return io.MultiReader(
  189. strings.NewReader(e.DeliveryHeader),
  190. bytes.NewReader(e.Data.Bytes()),
  191. )
  192. }
  193. // String converts the email to string.
  194. // Typically, you would want to use the compressor guerrilla.Processor for more efficiency, or use NewReader
  195. func (e *Envelope) String() string {
  196. return e.DeliveryHeader + e.Data.String()
  197. }
  198. // ResetTransaction is called when the transaction is reset (keeping the connection open)
  199. func (e *Envelope) ResetTransaction() {
  200. // ensure not processing by the backend, will only get lock if finished, otherwise block
  201. e.Lock()
  202. // got the lock, it means processing finished
  203. e.Unlock()
  204. e.MailFrom = Address{}
  205. e.RcptTo = []Address{}
  206. // reset the data buffer, keep it allocated
  207. e.Data.Reset()
  208. // todo: these are probably good candidates for buffers / use sync.Pool (after profiling)
  209. e.Subject = ""
  210. e.Header = nil
  211. e.Hashes = make([]string, 0)
  212. e.DeliveryHeader = ""
  213. e.Values = make(map[string]interface{})
  214. }
  215. // Reseed is called when used with a new connection, once it's accepted
  216. func (e *Envelope) Reseed(remoteIP string, clientID uint64) {
  217. e.RemoteIP = remoteIP
  218. e.QueuedId = queuedID(clientID)
  219. e.Helo = ""
  220. e.TLS = false
  221. e.ESMTP = false
  222. }
  223. // PushRcpt adds a recipient email address to the envelope
  224. func (e *Envelope) PushRcpt(addr Address) {
  225. e.RcptTo = append(e.RcptTo, addr)
  226. }
  227. // PopRcpt removes the last email address that was pushed to the envelope
  228. func (e *Envelope) PopRcpt() Address {
  229. ret := e.RcptTo[len(e.RcptTo)-1]
  230. e.RcptTo = e.RcptTo[:len(e.RcptTo)-1]
  231. return ret
  232. }
  233. const (
  234. statePlainText = iota
  235. stateStartEncodedWord
  236. stateEncodedWord
  237. stateEncoding
  238. stateCharset
  239. statePayload
  240. statePayloadEnd
  241. )
  242. // MimeHeaderDecode converts 7 bit encoded mime header strings to UTF-8
  243. func MimeHeaderDecode(str string) string {
  244. // optimized to only create an output buffer if there's need to
  245. // the `out` buffer is only made if an encoded word was decoded without error
  246. // `out` is made with the capacity of len(str)
  247. // a simple state machine is used to detect the start & end of encoded word and plain-text
  248. state := statePlainText
  249. var (
  250. out []byte
  251. wordStart int // start of an encoded word
  252. wordLen int // end of an encoded
  253. ptextStart = -1 // start of plan-text
  254. ptextLen int // end of plain-text
  255. )
  256. for i := 0; i < len(str); i++ {
  257. switch state {
  258. case statePlainText:
  259. if ptextStart == -1 {
  260. ptextStart = i
  261. }
  262. if str[i] == '=' {
  263. state = stateStartEncodedWord
  264. wordStart = i
  265. wordLen = 1
  266. } else {
  267. ptextLen++
  268. }
  269. case stateStartEncodedWord:
  270. if str[i] == '?' {
  271. wordLen++
  272. state = stateCharset
  273. } else {
  274. wordLen = 0
  275. state = statePlainText
  276. ptextLen++
  277. }
  278. case stateCharset:
  279. if str[i] == '?' {
  280. wordLen++
  281. state = stateEncoding
  282. } else if str[i] >= 'a' && str[i] <= 'z' ||
  283. str[i] >= 'A' && str[i] <= 'Z' ||
  284. str[i] >= '0' && str[i] <= '9' || str[i] == '-' {
  285. wordLen++
  286. } else {
  287. // error
  288. state = statePlainText
  289. ptextLen += wordLen
  290. wordLen = 0
  291. }
  292. case stateEncoding:
  293. if str[i] == '?' {
  294. wordLen++
  295. state = statePayload
  296. } else if str[i] == 'Q' || str[i] == 'q' || str[i] == 'b' || str[i] == 'B' {
  297. wordLen++
  298. } else {
  299. // abort
  300. state = statePlainText
  301. ptextLen += wordLen
  302. wordLen = 0
  303. }
  304. case statePayload:
  305. if str[i] == '?' {
  306. wordLen++
  307. state = statePayloadEnd
  308. } else {
  309. wordLen++
  310. }
  311. case statePayloadEnd:
  312. if str[i] == '=' {
  313. wordLen++
  314. var err error
  315. out, err = decodeWordAppend(ptextLen, out, str, ptextStart, wordStart, wordLen)
  316. if err != nil && out == nil {
  317. // special case: there was an error with decoding and `out` wasn't created
  318. // we can assume the encoded word as plaintext
  319. ptextLen += wordLen //+ 1 // add 1 for the space/tab
  320. wordLen = 0
  321. wordStart = 0
  322. state = statePlainText
  323. continue
  324. }
  325. if skip := hasEncodedWordAhead(str, i+1); skip != -1 {
  326. i = skip
  327. } else {
  328. out = makeAppend(out, len(str), []byte{})
  329. }
  330. ptextStart = -1
  331. ptextLen = 0
  332. wordLen = 0
  333. wordStart = 0
  334. state = statePlainText
  335. } else {
  336. // abort
  337. state = statePlainText
  338. ptextLen += wordLen
  339. wordLen = 0
  340. }
  341. }
  342. }
  343. if out != nil && ptextLen > 0 {
  344. out = makeAppend(out, len(str), []byte(str[ptextStart:ptextStart+ptextLen]))
  345. ptextLen = 0
  346. }
  347. if out == nil {
  348. // best case: there was nothing to encode
  349. return str
  350. }
  351. return string(out)
  352. }
  353. func decodeWordAppend(ptextLen int, out []byte, str string, ptextStart int, wordStart int, wordLen int) ([]byte, error) {
  354. if ptextLen > 0 {
  355. out = makeAppend(out, len(str), []byte(str[ptextStart:ptextStart+ptextLen]))
  356. }
  357. d, err := Dec.Decode(str[wordStart : wordLen+wordStart])
  358. if err == nil {
  359. out = makeAppend(out, len(str), []byte(d))
  360. } else if out != nil {
  361. out = makeAppend(out, len(str), []byte(str[wordStart:wordLen+wordStart]))
  362. }
  363. return out, err
  364. }
  365. func makeAppend(out []byte, size int, in []byte) []byte {
  366. if out == nil {
  367. out = make([]byte, 0, size)
  368. }
  369. out = append(out, in...)
  370. return out
  371. }
  372. func hasEncodedWordAhead(str string, i int) int {
  373. for ; i+2 < len(str); i++ {
  374. if str[i] != ' ' && str[i] != '\t' {
  375. return -1
  376. }
  377. if str[i+1] == '=' && str[i+2] == '?' {
  378. return i
  379. }
  380. }
  381. return -1
  382. }
  383. // Envelopes have their own pool
  384. type Pool struct {
  385. // envelopes that are ready to be borrowed
  386. pool chan *Envelope
  387. // semaphore to control number of maximum borrowed envelopes
  388. sem chan bool
  389. }
  390. func NewPool(poolSize int) *Pool {
  391. return &Pool{
  392. pool: make(chan *Envelope, poolSize),
  393. sem: make(chan bool, poolSize),
  394. }
  395. }
  396. func (p *Pool) Borrow(remoteAddr string, clientID uint64) *Envelope {
  397. var e *Envelope
  398. p.sem <- true // block the envelope until more room
  399. select {
  400. case e = <-p.pool:
  401. e.Reseed(remoteAddr, clientID)
  402. default:
  403. e = NewEnvelope(remoteAddr, clientID)
  404. }
  405. return e
  406. }
  407. // Return returns an envelope back to the envelope pool
  408. // Make sure that envelope finished processing before calling this
  409. func (p *Pool) Return(e *Envelope) {
  410. select {
  411. case p.pool <- e:
  412. //placed envelope back in pool
  413. default:
  414. // pool is full, discard it
  415. }
  416. // take a value off the semaphore to make room for more envelopes
  417. <-p.sem
  418. }