util.go 5.0 KB

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