action.go 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. package pop3_server
  2. import (
  3. "database/sql"
  4. "github.com/Jinnrry/gopop"
  5. "github.com/Jinnrry/pmail/consts"
  6. "github.com/Jinnrry/pmail/db"
  7. "github.com/Jinnrry/pmail/dto"
  8. "github.com/Jinnrry/pmail/models"
  9. "github.com/Jinnrry/pmail/services/del_email"
  10. "github.com/Jinnrry/pmail/services/detail"
  11. "github.com/Jinnrry/pmail/services/list"
  12. "github.com/Jinnrry/pmail/utils/array"
  13. "github.com/Jinnrry/pmail/utils/context"
  14. "github.com/Jinnrry/pmail/utils/errors"
  15. "github.com/Jinnrry/pmail/utils/id"
  16. "github.com/Jinnrry/pmail/utils/password"
  17. log "github.com/sirupsen/logrus"
  18. "github.com/spf13/cast"
  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. emailList, _ := list.GetEmailList(session.Ctx.(*context.Context), dto.SearchTag{Type: consts.EmailTypeReceive, Status: -1, GroupId: -1}, "", true, 0, 99999)
  152. for _, info := range emailList {
  153. res = append(res, listItem{
  154. Id: cast.ToInt64(info.Id),
  155. Size: cast.ToInt64(info.Size),
  156. })
  157. }
  158. ret := []gopop.UidlItem{}
  159. for _, re := range res {
  160. ret = append(ret, gopop.UidlItem{
  161. Id: re.Id,
  162. UnionId: cast.ToString(re.Id),
  163. })
  164. }
  165. return ret, nil
  166. }
  167. type listItem struct {
  168. Id int64 `json:"id"`
  169. Size int64 `json:"size"`
  170. }
  171. // List 邮件列表
  172. func (a action) List(session *gopop.Session, msg string) ([]gopop.MailInfo, error) {
  173. log.WithContext(session.Ctx).Debugf("POP3 CMD: LIST ,Args:%s", msg)
  174. var res []listItem
  175. var listId int
  176. if msg != "" {
  177. listId = cast.ToInt(msg)
  178. if listId == 0 {
  179. return nil, errors.New("params error")
  180. }
  181. }
  182. if listId != 0 {
  183. info, err := detail.GetEmailDetail(session.Ctx.(*context.Context), listId, false)
  184. if err != nil {
  185. return nil, err
  186. }
  187. res = append(res, listItem{
  188. Id: cast.ToInt64(info.Id),
  189. Size: cast.ToInt64(info.Size),
  190. })
  191. } else {
  192. emailList, _ := list.GetEmailList(session.Ctx.(*context.Context), dto.SearchTag{Type: consts.EmailTypeReceive, Status: -1, GroupId: -1}, "", true, 0, 99999)
  193. for _, info := range emailList {
  194. res = append(res, listItem{
  195. Id: cast.ToInt64(info.Id),
  196. Size: cast.ToInt64(info.Size),
  197. })
  198. }
  199. }
  200. ret := []gopop.MailInfo{}
  201. for _, re := range res {
  202. ret = append(ret, gopop.MailInfo{
  203. Id: re.Id,
  204. Size: re.Size,
  205. })
  206. }
  207. return ret, nil
  208. }
  209. // Retr 获取邮件详情
  210. func (a action) Retr(session *gopop.Session, id int64) (string, int64, error) {
  211. log.WithContext(session.Ctx).Debugf("POP3 CMD: RETR ,Args:%d", id)
  212. email, err := detail.GetEmailDetail(session.Ctx.(*context.Context), cast.ToInt(id), false)
  213. if err != nil {
  214. log.WithContext(session.Ctx.(*context.Context)).Errorf("%+v", err)
  215. return "", 0, errors.New("server error")
  216. }
  217. ret := email.ToTransObj().BuildBytes(session.Ctx.(*context.Context), false)
  218. return string(ret), cast.ToInt64(len(ret)), nil
  219. }
  220. // Delete 删除邮件
  221. func (a action) Delete(session *gopop.Session, id int64) error {
  222. log.WithContext(session.Ctx).Debugf("POP3 CMD: DELE ,Args:%d", id)
  223. session.DeleteIds = append(session.DeleteIds, id)
  224. session.DeleteIds = array.Unique(session.DeleteIds)
  225. return nil
  226. }
  227. func (a action) Rest(session *gopop.Session) error {
  228. log.WithContext(session.Ctx).Debugf("POP3 CMD: REST ")
  229. session.DeleteIds = []int64{}
  230. return nil
  231. }
  232. func (a action) Top(session *gopop.Session, id int64, n int) (string, error) {
  233. log.WithContext(session.Ctx).Debugf("POP3 CMD: TOP %d %d", id, n)
  234. email, err := detail.GetEmailDetail(session.Ctx.(*context.Context), cast.ToInt(id), false)
  235. if err != nil {
  236. log.WithContext(session.Ctx.(*context.Context)).Errorf("%+v", err)
  237. return "", errors.New("server error")
  238. }
  239. ret := email.ToTransObj().BuildBytes(session.Ctx.(*context.Context), false)
  240. res := strings.Split(string(ret), "\n")
  241. headerEndLine := len(res) - 1
  242. for i, re := range res {
  243. if re == "\r" {
  244. headerEndLine = i
  245. break
  246. }
  247. }
  248. if len(res) <= headerEndLine+n+1 {
  249. return string(ret), nil
  250. }
  251. return array.Join(res[0:headerEndLine+n+1], "\n"), nil
  252. }
  253. func (a action) Noop(session *gopop.Session) error {
  254. log.WithContext(session.Ctx).Debugf("POP3 CMD: NOOP ")
  255. return nil
  256. }
  257. func (a action) Quit(session *gopop.Session) error {
  258. log.WithContext(session.Ctx).Debugf("POP3 CMD: QUIT ")
  259. if len(session.DeleteIds) > 0 {
  260. del_email.DelEmail(session.Ctx.(*context.Context), session.DeleteIds, false)
  261. }
  262. return nil
  263. }