util.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. package util
  2. import (
  3. "bytes"
  4. "compress/zlib"
  5. "crypto/md5"
  6. "encoding/base64"
  7. "errors"
  8. "fmt"
  9. "io"
  10. "io/ioutil"
  11. "regexp"
  12. "strings"
  13. "gopkg.in/iconv.v1"
  14. "github.com/sloonz/go-qprintable"
  15. )
  16. type EmailParts struct {
  17. User string
  18. Host string
  19. }
  20. func ValidateEmailData(mailFrom, rcptTo string) (*EmailParts, *EmailParts, error) {
  21. var user, host string
  22. var addrErr error
  23. if user, host, addrErr = extractEmail(mailFrom); addrErr != nil {
  24. return nil, nil, addrErr
  25. }
  26. from := &EmailParts{User: user, Host: host}
  27. if user, host, addrErr = extractEmail(rcptTo); addrErr != nil {
  28. return nil, nil, addrErr
  29. }
  30. to := &EmailParts{User: user, Host: host}
  31. return from, to, nil
  32. }
  33. var extractEmailRegex, _ = regexp.Compile(`<(.+?)@(.+?)>`) // go home regex, you're drunk!
  34. func extractEmail(str string) (name string, host string, err error) {
  35. if matched := extractEmailRegex.FindStringSubmatch(str); len(matched) > 2 {
  36. host = validHost(matched[2])
  37. name = matched[1]
  38. } else {
  39. if res := strings.Split(str, "@"); len(res) > 1 {
  40. name = res[0]
  41. host = validHost(res[1])
  42. }
  43. }
  44. if host == "" || name == "" {
  45. err = errors.New("Invalid address, [" + name + "@" + host + "] address:" + str)
  46. }
  47. return name, host, err
  48. }
  49. var mimeRegex, _ = regexp.Compile(`=\?(.+?)\?([QBqp])\?(.+?)\?=`)
  50. // Decode strings in Mime header format
  51. // eg. =?ISO-2022-JP?B?GyRCIVo9dztSOWJAOCVBJWMbKEI=?=
  52. func MimeHeaderDecode(str string) string {
  53. matched := mimeRegex.FindAllStringSubmatch(str, -1)
  54. var charset, encoding, payload string
  55. if matched != nil {
  56. for i := 0; i < len(matched); i++ {
  57. if len(matched[i]) > 2 {
  58. charset = matched[i][1]
  59. encoding = strings.ToUpper(matched[i][2])
  60. payload = matched[i][3]
  61. switch encoding {
  62. case "B":
  63. str = strings.Replace(
  64. str,
  65. matched[i][0],
  66. MailTransportDecode(payload, "base64", charset),
  67. 1)
  68. case "Q":
  69. str = strings.Replace(
  70. str,
  71. matched[i][0],
  72. MailTransportDecode(payload, "quoted-printable", charset),
  73. 1)
  74. }
  75. }
  76. }
  77. }
  78. return str
  79. }
  80. var valihostRegex, _ = regexp.Compile(`^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$`)
  81. func validHost(host string) string {
  82. host = strings.Trim(host, " ")
  83. if valihostRegex.MatchString(host) {
  84. return host
  85. }
  86. return ""
  87. }
  88. // decode from 7bit to 8bit UTF-8
  89. // encodingType can be "base64" or "quoted-printable"
  90. func MailTransportDecode(str string, encodingType string, charset string) string {
  91. if charset == "" {
  92. charset = "UTF-8"
  93. } else {
  94. charset = strings.ToUpper(charset)
  95. }
  96. if encodingType == "base64" {
  97. str = fromBase64(str)
  98. } else if encodingType == "quoted-printable" {
  99. str = fromQuotedP(str)
  100. }
  101. if charset != "UTF-8" {
  102. charset = fixCharset(charset)
  103. if cd, err := iconv.Open("UTF-8", charset); err == nil {
  104. defer func() {
  105. cd.Close()
  106. if r := recover(); r != nil {
  107. //logln(1, fmt.Sprintf("Recovered in %v", r))
  108. }
  109. }()
  110. // eg. charset can be "ISO-2022-JP"
  111. return cd.ConvString(str)
  112. }
  113. }
  114. return str
  115. }
  116. func fromBase64(data string) string {
  117. buf := bytes.NewBufferString(data)
  118. decoder := base64.NewDecoder(base64.StdEncoding, buf)
  119. res, _ := ioutil.ReadAll(decoder)
  120. return string(res)
  121. }
  122. func fromQuotedP(data string) string {
  123. buf := bytes.NewBufferString(data)
  124. decoder := qprintable.NewDecoder(qprintable.BinaryEncoding, buf)
  125. res, _ := ioutil.ReadAll(decoder)
  126. return string(res)
  127. }
  128. var charsetRegex, _ = regexp.Compile(`[_:.\/\\]`)
  129. func fixCharset(charset string) string {
  130. fixed_charset := charsetRegex.ReplaceAllString(charset, "-")
  131. // Fix charset
  132. // borrowed from http://squirrelmail.svn.sourceforge.net/viewvc/squirrelmail/trunk/squirrelmail/include/languages.php?revision=13765&view=markup
  133. // OE ks_c_5601_1987 > cp949
  134. fixed_charset = strings.Replace(fixed_charset, "ks-c-5601-1987", "cp949", -1)
  135. // Moz x-euc-tw > euc-tw
  136. fixed_charset = strings.Replace(fixed_charset, "x-euc", "euc", -1)
  137. // Moz x-windows-949 > cp949
  138. fixed_charset = strings.Replace(fixed_charset, "x-windows_", "cp", -1)
  139. // windows-125x and cp125x charsets
  140. fixed_charset = strings.Replace(fixed_charset, "windows-", "cp", -1)
  141. // ibm > cp
  142. fixed_charset = strings.Replace(fixed_charset, "ibm", "cp", -1)
  143. // iso-8859-8-i -> iso-8859-8
  144. fixed_charset = strings.Replace(fixed_charset, "iso-8859-8-i", "iso-8859-8", -1)
  145. if charset != fixed_charset {
  146. return fixed_charset
  147. }
  148. return charset
  149. }
  150. // returns an md5 hash as string of hex characters
  151. func MD5Hex(stringArguments ...*string) string {
  152. h := md5.New()
  153. var r *strings.Reader
  154. for i := 0; i < len(stringArguments); i++ {
  155. r = strings.NewReader(*stringArguments[i])
  156. io.Copy(h, r)
  157. }
  158. sum := h.Sum([]byte{})
  159. return fmt.Sprintf("%x", sum)
  160. }
  161. // concatenate & compress all strings passed in
  162. func Compress(stringArguments ...*string) string {
  163. var b bytes.Buffer
  164. var r *strings.Reader
  165. w, _ := zlib.NewWriterLevel(&b, zlib.BestSpeed)
  166. for i := 0; i < len(stringArguments); i++ {
  167. r = strings.NewReader(*stringArguments[i])
  168. io.Copy(w, r)
  169. }
  170. w.Close()
  171. return b.String()
  172. }