A temporary email service written in Go.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

107 lines
2.3 KiB

  1. package mail
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "io/ioutil"
  7. "log"
  8. "mime"
  9. "mime/multipart"
  10. "net"
  11. "net/mail"
  12. "os"
  13. "strings"
  14. "github.com/mhale/smtpd"
  15. "github.com/thebaer/burner/validate"
  16. )
  17. var (
  18. mailInfo *log.Logger
  19. mailError *log.Logger
  20. )
  21. type MailConfig struct {
  22. Port int
  23. Host string
  24. }
  25. var mailCfg *MailConfig
  26. func Serve(host string, port int) error {
  27. mailCfg = &MailConfig{
  28. Port: port,
  29. Host: host,
  30. }
  31. // Set up variables
  32. mailInfo = log.New(os.Stdout, "", log.Ldate|log.Ltime)
  33. mailError = log.New(os.Stderr, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)
  34. // Start mail server
  35. mailInfo.Printf("Starting %s mail server on :%d", mailCfg.Host, mailCfg.Port)
  36. err := smtpd.ListenAndServe(fmt.Sprintf(":%d", mailCfg.Port), mailHandler, mailCfg.Host, mailCfg.Host)
  37. if err != nil {
  38. mailError.Printf("Couldn't start mail server: %v", err)
  39. return err
  40. }
  41. return nil
  42. }
  43. func mailHandler(origin net.Addr, from string, to []string, data []byte) {
  44. msg, err := mail.ReadMessage(bytes.NewReader(data))
  45. if err != nil {
  46. mailError.Printf("Couldn't read email message: %v", err)
  47. return
  48. }
  49. var content []byte
  50. subject := msg.Header.Get("Subject")
  51. contentType, params, err := mime.ParseMediaType(msg.Header.Get("Content-Type"))
  52. if err != nil {
  53. mailError.Printf("Couldn't parse Content-Type: %v", err)
  54. }
  55. if strings.HasPrefix(contentType, "multipart/") {
  56. mr := multipart.NewReader(msg.Body, params["boundary"])
  57. for {
  58. p, err := mr.NextPart()
  59. if err == io.EOF {
  60. break
  61. }
  62. if err != nil {
  63. mailError.Printf("Error getting NextPart(): %v", err)
  64. continue
  65. }
  66. if !strings.HasPrefix(p.Header.Get("Content-Type"), "text/plain") {
  67. continue
  68. }
  69. // We correctly read the text/plain section of email
  70. content, err = ioutil.ReadAll(p)
  71. if err != nil {
  72. mailError.Printf("Error in ReadAll on part: %v", err)
  73. }
  74. break
  75. }
  76. } else {
  77. var err error
  78. content, err = ioutil.ReadAll(msg.Body)
  79. if err != nil {
  80. mailError.Printf("Couldn't read email body: %v", err)
  81. return
  82. }
  83. }
  84. mailInfo.Printf("Received mail from %s for %s with subject %s", from, to[0], subject)
  85. createPostFromEmail(to[0], subject, from, content)
  86. }
  87. func createPostFromEmail(to, subject, from string, content []byte) {
  88. if err := validate.Email(to); err != nil {
  89. return
  90. }
  91. }