action.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. package pop3_server
  2. import (
  3. "database/sql"
  4. "github.com/Jinnrry/gopop"
  5. log "github.com/sirupsen/logrus"
  6. "github.com/spf13/cast"
  7. "pmail/consts"
  8. "pmail/db"
  9. "pmail/dto"
  10. "pmail/models"
  11. "pmail/services/del_email"
  12. "pmail/services/detail"
  13. "pmail/services/list"
  14. "pmail/utils/array"
  15. "pmail/utils/context"
  16. "pmail/utils/errors"
  17. "pmail/utils/id"
  18. "pmail/utils/password"
  19. "strings"
  20. )
  21. type action struct {
  22. }
  23. // Custom 非标准命令
  24. func (a action) Custom(session *gopop.Session, cmd string, args []string) ([]string, error) {
  25. if session.Ctx == nil {
  26. tc := &context.Context{}
  27. tc.SetValue(context.LogID, id.GenLogID())
  28. session.Ctx = tc
  29. }
  30. log.WithContext(session.Ctx).Warnf("not supported cmd request! cmd:%s args:%v", cmd, args)
  31. return nil, nil
  32. }
  33. // Capa 说明服务端支持的命令列表
  34. func (a action) Capa(session *gopop.Session) ([]string, error) {
  35. if session.Ctx == nil {
  36. tc := &context.Context{}
  37. tc.SetValue(context.LogID, id.GenLogID())
  38. session.Ctx = tc
  39. }
  40. if session.InTls {
  41. log.WithContext(session.Ctx).Debugf("POP3 CMD: CAPA With Tls")
  42. } else {
  43. log.WithContext(session.Ctx).Debugf("POP3 CMD: CAPA Without Tls")
  44. }
  45. ret := []string{
  46. "USER",
  47. "PASS",
  48. "TOP",
  49. "APOP",
  50. "STAT",
  51. "UIDL",
  52. "LIST",
  53. "RETR",
  54. "DELE",
  55. "REST",
  56. "NOOP",
  57. "QUIT",
  58. }
  59. if !session.InTls {
  60. ret = append(ret, "STLS")
  61. }
  62. return ret, nil
  63. }
  64. // User 提交登陆的用户名
  65. func (a action) User(session *gopop.Session, username string) error {
  66. if session.Ctx == nil {
  67. tc := &context.Context{}
  68. tc.SetValue(context.LogID, id.GenLogID())
  69. session.Ctx = tc
  70. }
  71. log.WithContext(session.Ctx).Debugf("POP3 CMD: USER, Args:%s", username)
  72. infos := strings.Split(username, "@")
  73. if len(infos) > 1 {
  74. username = infos[0]
  75. }
  76. log.WithContext(session.Ctx).Debugf("POP3 User %s", username)
  77. session.User = username
  78. return nil
  79. }
  80. // Pass 提交密码验证
  81. func (a action) Pass(session *gopop.Session, pwd string) error {
  82. if session.Ctx == nil {
  83. tc := &context.Context{}
  84. tc.SetValue(context.LogID, id.GenLogID())
  85. session.Ctx = tc
  86. }
  87. log.WithContext(session.Ctx).Debugf("POP3 PASS %s , User:%s", pwd, session.User)
  88. var user models.User
  89. encodePwd := password.Encode(pwd)
  90. _, err := db.Instance.Where("account =? and password =? and disabled = 0", session.User, encodePwd).Get(&user)
  91. if err != nil && !errors.Is(err, sql.ErrNoRows) {
  92. log.WithContext(session.Ctx.(*context.Context)).Errorf("%+v", err)
  93. }
  94. if user.ID > 0 {
  95. session.Status = gopop.TRANSACTION
  96. session.Ctx.(*context.Context).UserID = user.ID
  97. session.Ctx.(*context.Context).UserName = user.Name
  98. session.Ctx.(*context.Context).UserAccount = user.Account
  99. return nil
  100. }
  101. return errors.New("password error")
  102. }
  103. // Apop APOP登陆命令
  104. func (a action) Apop(session *gopop.Session, username, digest string) error {
  105. if session.Ctx == nil {
  106. tc := &context.Context{}
  107. tc.SetValue(context.LogID, id.GenLogID())
  108. session.Ctx = tc
  109. }
  110. log.WithContext(session.Ctx).Debugf("POP3 CMD: APOP, Args:%s,%s", username, digest)
  111. infos := strings.Split(username, "@")
  112. if len(infos) > 1 {
  113. username = infos[0]
  114. }
  115. log.WithContext(session.Ctx).Debugf("POP3 APOP %s %s", username, digest)
  116. var user models.User
  117. _, err := db.Instance.Where("account =? and disabled = 0", username).Get(&user)
  118. if err != nil && !errors.Is(err, sql.ErrNoRows) {
  119. log.WithContext(session.Ctx.(*context.Context)).Errorf("%+v", err)
  120. }
  121. if user.ID > 0 && digest == password.Md5Encode(user.Password) {
  122. session.User = username
  123. session.Status = gopop.TRANSACTION
  124. session.Ctx.(*context.Context).UserID = user.ID
  125. session.Ctx.(*context.Context).UserName = user.Name
  126. session.Ctx.(*context.Context).UserAccount = user.Account
  127. return nil
  128. }
  129. return errors.New("password error")
  130. }
  131. // Stat 查询邮件数量
  132. func (a action) Stat(session *gopop.Session) (msgNum, msgSize int64, err error) {
  133. log.WithContext(session.Ctx).Debugf("POP3 CMD: STAT")
  134. num, size := list.Stat(session.Ctx.(*context.Context))
  135. log.WithContext(session.Ctx).Debugf("POP3 STAT RETURT : %d,%d", num, size)
  136. return num, size, nil
  137. }
  138. // Uidl 查询某封邮件的唯一标志符
  139. func (a action) Uidl(session *gopop.Session, msg string) ([]gopop.UidlItem, error) {
  140. log.WithContext(session.Ctx).Debugf("POP3 CMD: UIDL ,Args:%s", msg)
  141. reqId := cast.ToInt64(msg)
  142. if reqId > 0 {
  143. return []gopop.UidlItem{
  144. {
  145. Id: reqId,
  146. UnionId: msg,
  147. },
  148. }, nil
  149. }
  150. var res []listItem
  151. var err error
  152. var ssql string
  153. err = db.Instance.Where("type=0 and status=0").Select("id").Table("email").Find(&res)
  154. if err != nil && !errors.Is(err, sql.ErrNoRows) {
  155. log.WithContext(session.Ctx.(*context.Context)).Errorf("SQL:%s Error: %+v", ssql, err)
  156. err = nil
  157. return []gopop.UidlItem{}, nil
  158. }
  159. ret := []gopop.UidlItem{}
  160. for _, re := range res {
  161. ret = append(ret, gopop.UidlItem{
  162. Id: re.Id,
  163. UnionId: cast.ToString(re.Id),
  164. })
  165. }
  166. return ret, nil
  167. }
  168. type listItem struct {
  169. Id int64 `json:"id"`
  170. Size int64 `json:"size"`
  171. }
  172. // List 邮件列表
  173. func (a action) List(session *gopop.Session, msg string) ([]gopop.MailInfo, error) {
  174. log.WithContext(session.Ctx).Debugf("POP3 CMD: LIST ,Args:%s", msg)
  175. var res []listItem
  176. var listId int
  177. if msg != "" {
  178. listId = cast.ToInt(msg)
  179. if listId == 0 {
  180. return nil, errors.New("params error")
  181. }
  182. }
  183. if listId != 0 {
  184. info, err := detail.GetEmailDetail(session.Ctx.(*context.Context), listId, false)
  185. if err != nil {
  186. return nil, err
  187. }
  188. res = append(res, listItem{
  189. Id: cast.ToInt64(info.Id),
  190. Size: cast.ToInt64(info.Size),
  191. })
  192. } else {
  193. emailList, _ := list.GetEmailList(session.Ctx.(*context.Context), dto.SearchTag{Type: consts.EmailTypeReceive, Status: -1, GroupId: -1}, "", true, 0, 99999)
  194. for _, info := range emailList {
  195. res = append(res, listItem{
  196. Id: cast.ToInt64(info.Id),
  197. Size: cast.ToInt64(info.Size),
  198. })
  199. }
  200. }
  201. ret := []gopop.MailInfo{}
  202. for _, re := range res {
  203. ret = append(ret, gopop.MailInfo{
  204. Id: re.Id,
  205. Size: re.Size,
  206. })
  207. }
  208. return ret, nil
  209. }
  210. // Retr 获取邮件详情
  211. func (a action) Retr(session *gopop.Session, id int64) (string, int64, error) {
  212. log.WithContext(session.Ctx).Debugf("POP3 CMD: RETR ,Args:%d", id)
  213. email, err := detail.GetEmailDetail(session.Ctx.(*context.Context), cast.ToInt(id), false)
  214. if err != nil {
  215. log.WithContext(session.Ctx.(*context.Context)).Errorf("%+v", err)
  216. return "", 0, errors.New("server error")
  217. }
  218. ret := email.ToTransObj().BuildBytes(session.Ctx.(*context.Context), false)
  219. return string(ret), cast.ToInt64(len(ret)), nil
  220. }
  221. // Delete 删除邮件
  222. func (a action) Delete(session *gopop.Session, id int64) error {
  223. log.WithContext(session.Ctx).Debugf("POP3 CMD: DELE ,Args:%d", id)
  224. session.DeleteIds = append(session.DeleteIds, id)
  225. session.DeleteIds = array.Unique(session.DeleteIds)
  226. return nil
  227. }
  228. func (a action) Rest(session *gopop.Session) error {
  229. log.WithContext(session.Ctx).Debugf("POP3 CMD: REST ")
  230. session.DeleteIds = []int64{}
  231. return nil
  232. }
  233. func (a action) Top(session *gopop.Session, id int64, n int) (string, error) {
  234. log.WithContext(session.Ctx).Debugf("POP3 CMD: TOP %d %d", id, n)
  235. email, err := detail.GetEmailDetail(session.Ctx.(*context.Context), cast.ToInt(id), false)
  236. if err != nil {
  237. log.WithContext(session.Ctx.(*context.Context)).Errorf("%+v", err)
  238. return "", errors.New("server error")
  239. }
  240. ret := email.ToTransObj().BuildBytes(session.Ctx.(*context.Context), false)
  241. res := strings.Split(string(ret), "\n")
  242. headerEndLine := len(res) - 1
  243. for i, re := range res {
  244. if re == "\r" {
  245. headerEndLine = i
  246. break
  247. }
  248. }
  249. if len(res) <= headerEndLine+n+1 {
  250. return string(ret), nil
  251. }
  252. return array.Join(res[0:headerEndLine+n+1], "\n"), nil
  253. }
  254. func (a action) Noop(session *gopop.Session) error {
  255. log.WithContext(session.Ctx).Debugf("POP3 CMD: NOOP ")
  256. return nil
  257. }
  258. func (a action) Quit(session *gopop.Session) error {
  259. log.WithContext(session.Ctx).Debugf("POP3 CMD: QUIT ")
  260. if len(session.DeleteIds) > 0 {
  261. del_email.DelEmailI64(session.Ctx.(*context.Context), session.DeleteIds)
  262. }
  263. return nil
  264. }