imap.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. package goimap
  2. import (
  3. "bufio"
  4. "crypto/tls"
  5. "errors"
  6. "fmt"
  7. log "github.com/sirupsen/logrus"
  8. "io"
  9. "log/slog"
  10. "net"
  11. "strings"
  12. "sync"
  13. "time"
  14. )
  15. var (
  16. eol = "\r\n"
  17. )
  18. // Server Imap服务实例
  19. type Server struct {
  20. Domain string // 域名
  21. Port int // 端口
  22. TlsEnabled bool //是否启用Tls
  23. TlsConfig *tls.Config // tls配置
  24. ConnectAliveTime time.Duration // 连接存活时间,默认不超时
  25. Action Action
  26. stop chan bool
  27. close bool
  28. lck sync.Mutex
  29. }
  30. // NewImapServer 新建一个服务实例
  31. func NewImapServer(port int, domain string, tlsEnabled bool, tlsConfig *tls.Config, action Action) *Server {
  32. return &Server{
  33. Domain: domain,
  34. Port: port,
  35. TlsEnabled: tlsEnabled,
  36. TlsConfig: tlsConfig,
  37. Action: action,
  38. stop: make(chan bool, 1),
  39. }
  40. }
  41. // Start 启动服务
  42. func (s *Server) Start() error {
  43. if !s.TlsEnabled {
  44. return s.startWithoutTLS()
  45. } else {
  46. return s.startWithTLS()
  47. }
  48. }
  49. func (s *Server) startWithTLS() error {
  50. if s.lck.TryLock() {
  51. listener, err := tls.Listen("tcp", fmt.Sprintf(":%d", s.Port), s.TlsConfig)
  52. if err != nil {
  53. return err
  54. }
  55. s.close = false
  56. defer func() {
  57. listener.Close()
  58. }()
  59. go func() {
  60. for {
  61. conn, err := listener.Accept()
  62. if err != nil {
  63. if s.close {
  64. break
  65. } else {
  66. continue
  67. }
  68. }
  69. go s.handleClient(conn)
  70. }
  71. }()
  72. <-s.stop
  73. } else {
  74. return errors.New("Server Is Running")
  75. }
  76. return nil
  77. }
  78. func (s *Server) startWithoutTLS() error {
  79. if s.lck.TryLock() {
  80. listener, err := net.Listen("tcp", fmt.Sprintf(":%d", s.Port))
  81. if err != nil {
  82. return err
  83. }
  84. s.close = false
  85. defer func() {
  86. listener.Close()
  87. }()
  88. go func() {
  89. for {
  90. conn, err := listener.Accept()
  91. if err != nil {
  92. if s.close {
  93. break
  94. } else {
  95. continue
  96. }
  97. }
  98. go s.handleClient(conn)
  99. }
  100. }()
  101. <-s.stop
  102. } else {
  103. return errors.New("Server Is Running")
  104. }
  105. return nil
  106. }
  107. // Stop 停止服务
  108. func (s *Server) Stop() {
  109. s.close = true
  110. s.stop <- true
  111. }
  112. func (s *Server) handleClient(conn net.Conn) {
  113. slog.Debug("Imap conn")
  114. defer func() {
  115. if conn != nil {
  116. _ = conn.Close()
  117. }
  118. }()
  119. session := &Session{
  120. Conn: conn,
  121. Status: UNAUTHORIZED,
  122. AliveTime: time.Now(),
  123. }
  124. if s.TlsEnabled && s.TlsConfig != nil {
  125. session.InTls = true
  126. }
  127. // 检查连接是否超时
  128. if s.ConnectAliveTime != 0 {
  129. go func() {
  130. for {
  131. if time.Now().Sub(session.AliveTime) >= s.ConnectAliveTime {
  132. if session.Conn != nil {
  133. write(session.Conn, "* BYE AutoLogout; idle for too long", "")
  134. _ = session.Conn.Close()
  135. }
  136. session.Conn = nil
  137. session.IN_IDLE = false
  138. return
  139. }
  140. time.Sleep(3 * time.Second)
  141. }
  142. }()
  143. }
  144. reader := bufio.NewReader(conn)
  145. write(conn, `* OK [CAPABILITY IMAP4 IMAP4rev1 AUTH=PLAIN AUTH=LOGIN] PMail Server ready`, "")
  146. for {
  147. rawLine, err := reader.ReadString('\n')
  148. if err != nil {
  149. if conn != nil {
  150. _ = conn.Close()
  151. }
  152. session.Conn = nil
  153. session.IN_IDLE = false
  154. return
  155. }
  156. session.AliveTime = time.Now()
  157. nub, cmd, args := getCommand(rawLine)
  158. log.Debugf("Imap Input:\t %s", rawLine)
  159. if cmd != "IDLE" {
  160. session.IN_IDLE = false
  161. }
  162. switch cmd {
  163. case "":
  164. if conn != nil {
  165. conn.Close()
  166. conn = nil
  167. }
  168. break
  169. case "CAPABILITY":
  170. commands, err := s.Action.CapaBility(session)
  171. if err != nil {
  172. write(conn, fmt.Sprintf("* BAD %s%s", err.Error(), eol), nub)
  173. } else {
  174. ret := "*"
  175. for _, command := range commands {
  176. ret += " " + command
  177. }
  178. write(conn, ret, nub)
  179. showSucc(conn, nub)
  180. }
  181. case "CREATE":
  182. if session.Status != AUTHORIZED {
  183. showBad(conn, errors.New("Need Login"), nub)
  184. break
  185. }
  186. if args == "" {
  187. paramsErr(conn, "CREATE", nub)
  188. break
  189. }
  190. err := s.Action.Create(session, args)
  191. output(conn, nub, err)
  192. case "DELETE":
  193. if session.Status != AUTHORIZED {
  194. showBad(conn, errors.New("Need Login"), nub)
  195. break
  196. }
  197. if args == "" {
  198. paramsErr(conn, "DELETE", nub)
  199. break
  200. }
  201. err := s.Action.Delete(session, args)
  202. output(conn, nub, err)
  203. case "RENAME":
  204. if session.Status != AUTHORIZED {
  205. showBad(conn, errors.New("Need Login"), nub)
  206. break
  207. }
  208. if args == "" {
  209. paramsErr(conn, "RENAME", nub)
  210. } else {
  211. dt := strings.Split(args, " ")
  212. err := s.Action.Rename(session, dt[0], dt[1])
  213. output(conn, nub, err)
  214. }
  215. case "LIST":
  216. if session.Status != AUTHORIZED {
  217. showBad(conn, errors.New("Need Login"), nub)
  218. break
  219. }
  220. if args == "" {
  221. paramsErr(conn, "LIST", nub)
  222. } else {
  223. dt := strings.Split(args, " ")
  224. dt[0] = strings.ReplaceAll(dt[0], `"`, "")
  225. rets, err := s.Action.List(session, dt[0], dt[1])
  226. if err != nil {
  227. showBad(conn, err, nub)
  228. } else {
  229. ret := ""
  230. for _, str := range rets {
  231. ret += str + eol
  232. }
  233. write(conn, ret, nub)
  234. showSucc(conn, nub)
  235. }
  236. }
  237. case "APPEND":
  238. if session.Status != AUTHORIZED {
  239. showBad(conn, errors.New("Need Login"), nub)
  240. break
  241. }
  242. log.Debugf("Append: %+v", args)
  243. case "SELECT":
  244. if session.Status != AUTHORIZED {
  245. showBad(conn, errors.New("Need Login"), nub)
  246. break
  247. }
  248. ret, err := s.Action.Select(session, args)
  249. args = strings.ReplaceAll(args, `"`, "")
  250. if err != nil {
  251. showBad(conn, err, nub)
  252. } else {
  253. for _, s2 := range ret {
  254. write(conn, s2, nub)
  255. }
  256. }
  257. case "FETCH":
  258. if session.Status != AUTHORIZED {
  259. showBad(conn, errors.New("Need Login"), nub)
  260. break
  261. }
  262. if args == "" {
  263. paramsErr(conn, "RENAME", nub)
  264. } else {
  265. dt := strings.Split(args, " ")
  266. ret, err := s.Action.Fetch(session, dt[0], dt[1])
  267. if err != nil {
  268. showBad(conn, err, nub)
  269. } else {
  270. write(conn, ret, nub)
  271. showSucc(conn, ret)
  272. }
  273. }
  274. case "STORE":
  275. if session.Status != AUTHORIZED {
  276. showBad(conn, errors.New("Need Login"), nub)
  277. break
  278. }
  279. if args == "" {
  280. paramsErr(conn, "RENAME", nub)
  281. } else {
  282. dt := strings.Split(args, " ")
  283. err := s.Action.Store(session, dt[0], dt[1])
  284. output(conn, nub, err)
  285. }
  286. case "CLOSE":
  287. err := s.Action.Close(session)
  288. output(conn, nub, err)
  289. case "EXPUNGE":
  290. if session.Status != AUTHORIZED {
  291. showBad(conn, errors.New("Need Login"), nub)
  292. break
  293. }
  294. err := s.Action.Expunge(session)
  295. output(conn, nub, err)
  296. case "EXAMINE":
  297. if session.Status != AUTHORIZED {
  298. showBad(conn, errors.New("Need Login"), nub)
  299. break
  300. }
  301. if args == "" {
  302. paramsErr(conn, "EXAMINE", nub)
  303. }
  304. err := s.Action.Examine(session, args)
  305. output(conn, nub, err)
  306. case "SUBSCRIBE":
  307. if session.Status != AUTHORIZED {
  308. showBad(conn, errors.New("Need Login"), nub)
  309. break
  310. }
  311. if args == "" {
  312. paramsErr(conn, "SUBSCRIBE", nub)
  313. } else {
  314. err := s.Action.Subscribe(session, args)
  315. output(conn, nub, err)
  316. }
  317. case "UNSUBSCRIBE":
  318. if session.Status != AUTHORIZED {
  319. showBad(conn, errors.New("Need Login"), nub)
  320. break
  321. }
  322. if args == "" {
  323. paramsErr(conn, "UNSUBSCRIBE", nub)
  324. } else {
  325. err := s.Action.UnSubscribe(session, args)
  326. output(conn, nub, err)
  327. }
  328. case "LSUB":
  329. if session.Status != AUTHORIZED {
  330. showBad(conn, errors.New("Need Login"), nub)
  331. break
  332. }
  333. if args == "" {
  334. paramsErr(conn, "LSUB", nub)
  335. } else {
  336. dt := strings.Split(args, " ")
  337. rets, err := s.Action.LSub(session, dt[0], dt[1])
  338. if err != nil {
  339. showBad(conn, err, nub)
  340. } else {
  341. ret := ""
  342. for _, str := range rets {
  343. ret += str + eol
  344. }
  345. write(conn, ret, nub)
  346. showSucc(conn, nub)
  347. }
  348. }
  349. case "STATUS":
  350. if session.Status != AUTHORIZED {
  351. showBad(conn, errors.New("Need Login"), nub)
  352. break
  353. }
  354. if args == "" {
  355. paramsErr(conn, "STATUS", nub)
  356. } else {
  357. dt := strings.SplitN(args, " ", 2)
  358. dt[0] = strings.ReplaceAll(dt[0], `"`, "")
  359. dt[1] = strings.Trim(dt[1], "()")
  360. params := strings.Split(dt[1], " ")
  361. ret, err := s.Action.Status(session, dt[0], params)
  362. if err != nil {
  363. showBad(conn, err, nub)
  364. } else {
  365. write(conn, ret, nub)
  366. showSucc(conn, nub)
  367. }
  368. }
  369. case "CHECK":
  370. if session.Status != AUTHORIZED {
  371. showBad(conn, errors.New("Need Login"), nub)
  372. break
  373. }
  374. err := s.Action.Check(session)
  375. output(conn, nub, err)
  376. case "SEARCH":
  377. if session.Status != AUTHORIZED {
  378. showBad(conn, errors.New("Need Login"), nub)
  379. break
  380. }
  381. if args == "" {
  382. paramsErr(conn, "SEARCH", nub)
  383. } else {
  384. dt := strings.SplitN(args, " ", 2)
  385. ret, err := s.Action.Search(session, dt[0], dt[1])
  386. if err != nil {
  387. showBad(conn, err, nub)
  388. } else {
  389. write(conn, ret, nub)
  390. showSucc(conn, nub)
  391. }
  392. }
  393. case "COPY":
  394. if session.Status != AUTHORIZED {
  395. showBad(conn, errors.New("Need Login"), nub)
  396. break
  397. }
  398. if args == "" {
  399. paramsErr(conn, "COPY", nub)
  400. } else {
  401. dt := strings.SplitN(args, " ", 2)
  402. err := s.Action.Copy(session, dt[0], dt[1])
  403. output(conn, nub, err)
  404. }
  405. case "NOOP":
  406. err := s.Action.Noop(session)
  407. output(conn, nub, err)
  408. case "LOGIN":
  409. if args == "" {
  410. paramsErr(conn, "LOGIN", nub)
  411. } else {
  412. dt := strings.SplitN(args, " ", 2)
  413. err := s.Action.Login(session, dt[0], dt[1])
  414. output(conn, nub, err)
  415. }
  416. case "LOGOUT":
  417. err := s.Action.Logout(session)
  418. write(conn, "* BYE PMail Server logging out", nub)
  419. output(conn, nub, err)
  420. if conn != nil {
  421. _ = conn.Close()
  422. }
  423. case "UNSELECT":
  424. if session.Status != AUTHORIZED {
  425. showBad(conn, errors.New("Need Login"), nub)
  426. break
  427. }
  428. err := s.Action.Unselect(session)
  429. output(conn, nub, err)
  430. case "IDLE":
  431. if session.Status != AUTHORIZED {
  432. showBad(conn, errors.New("Need Login"), nub)
  433. break
  434. }
  435. session.IN_IDLE = true
  436. err := s.Action.IDLE(session)
  437. if err != nil {
  438. write(conn, fmt.Sprintf("+ idling%s", eol), nub)
  439. } else {
  440. showBad(conn, err, nub)
  441. }
  442. default:
  443. rets, err := s.Action.Custom(session, cmd, args)
  444. if err != nil {
  445. write(conn, fmt.Sprintf("* BAD %s %s", err.Error(), eol), nub)
  446. } else {
  447. if len(rets) == 0 {
  448. write(conn, fmt.Sprintf("%s OK %s", nub, eol), nub)
  449. } else if len(rets) == 1 {
  450. write(conn, fmt.Sprintf("%s OK %s%s", nub, rets[0], eol), nub)
  451. } else {
  452. ret := fmt.Sprintf("%s OK %s", nub, eol)
  453. for _, re := range rets {
  454. ret += fmt.Sprintf("%s%s", re, eol)
  455. }
  456. ret += "." + eol
  457. write(conn, fmt.Sprintf(ret), nub)
  458. }
  459. }
  460. }
  461. }
  462. }
  463. // cuts the line into command and arguments
  464. func getCommand(line string) (string, string, string) {
  465. line = strings.Trim(line, "\r \n")
  466. cmd := strings.SplitN(line, " ", 3)
  467. if len(cmd) == 1 {
  468. return "", "", ""
  469. }
  470. for i, s := range cmd {
  471. cmd[i] = s
  472. }
  473. if len(cmd) == 3 {
  474. return strings.ToTitle(cmd[0]), strings.ToTitle(cmd[1]), cmd[2]
  475. }
  476. return strings.ToTitle(cmd[0]), strings.ToTitle(cmd[1]), ""
  477. }
  478. func getSafeArg(args []string, nr int) string {
  479. if nr < len(args) {
  480. return args[nr]
  481. }
  482. return ""
  483. }
  484. func showSucc(w io.Writer, nub string) {
  485. write(w, fmt.Sprintf("%s OK success %s", nub, eol), nub)
  486. }
  487. func showBad(w io.Writer, err error, nub string) {
  488. if err == nil {
  489. write(w, fmt.Sprintf("* BAD %s", eol), nub)
  490. return
  491. }
  492. write(w, fmt.Sprintf("* BAD %s%s", err.Error(), eol), nub)
  493. }
  494. func output(w io.Writer, nub string, err error) {
  495. if err != nil {
  496. showBad(w, err, nub)
  497. } else {
  498. showSucc(w, nub)
  499. }
  500. }
  501. func paramsErr(w io.Writer, commend string, nub string) {
  502. write(w, fmt.Sprintf("* BAD %s parameters! %s", commend, eol), nub)
  503. }
  504. func write(w io.Writer, content string, nub string) {
  505. content = strings.ReplaceAll(content, "$$NUM", nub)
  506. log.Debugf("Imap Out:\t |%s", content)
  507. fmt.Fprintf(w, content)
  508. }