jinnrry 1 год назад
Родитель
Сommit
0867791eb7

+ 0 - 0
server/cron_server/ssl_update.go → server/listen/cron_server/ssl_update.go


+ 0 - 0
server/http_server/http_server.go → server/listen/http_server/http_server.go


+ 0 - 0
server/http_server/https_server.go → server/listen/http_server/https_server.go


+ 0 - 0
server/http_server/setup_server.go → server/listen/http_server/setup_server.go


+ 123 - 0
server/listen/imap_server/action.go

@@ -0,0 +1,123 @@
+package imap_server
+
+import (
+	"github.com/Jinnrry/pmail/listen/imap_server/goimap"
+	log "github.com/sirupsen/logrus"
+)
+
+type action struct{}
+
+func (a action) Create(session *goimap.Session, path string) error {
+	log.Infof("%s,%s", "Create", path)
+	return nil
+}
+
+func (a action) Delete(session *goimap.Session, path string) error {
+	log.Infof("%s,%s", "Create", path)
+	return nil
+}
+
+func (a action) Rename(session *goimap.Session, oldPath, newPath string) error {
+	log.Infof("%s,%s,%s", "Create", oldPath, newPath)
+	return nil
+}
+
+func (a action) List(session *goimap.Session, basePath, template string) ([]string, error) {
+	log.Infof("%s,%s,%s", "Create", basePath, template)
+	return nil, nil
+}
+
+func (a action) Append(session *goimap.Session, item string) error {
+	log.Infof("%s,%s", "Create", item)
+	return nil
+}
+
+func (a action) Select(session *goimap.Session, path string) error {
+	log.Infof("%s,%s", "Create", path)
+	return nil
+}
+
+func (a action) Fetch(session *goimap.Session, mailIds, dataNames string) (string, error) {
+	log.Infof("%s,%s,%s", "Fetch", mailIds, dataNames)
+	return "", nil
+}
+
+func (a action) Store(session *goimap.Session, mailId, flags string) error {
+	log.Infof("%s,%s,%s", "Store", mailId, flags)
+	return nil
+}
+
+func (a action) Close(session *goimap.Session) error {
+	log.Infof("%s", "Close")
+	return nil
+}
+
+func (a action) Expunge(session *goimap.Session) error {
+	log.Infof("%s", "Expunge")
+	return nil
+}
+
+func (a action) Examine(session *goimap.Session, path string) error {
+	log.Infof("%s,%s", "Examine", path)
+	return nil
+}
+
+func (a action) Subscribe(session *goimap.Session, path string) error {
+	log.Infof("%s,%s", "Subscribe", path)
+	return nil
+}
+
+func (a action) UnSubscribe(session *goimap.Session, path string) error {
+	log.Infof("%s,%s", "UnSubscribe", path)
+	return nil
+}
+
+func (a action) LSub(session *goimap.Session, path, mailbox string) ([]string, error) {
+	log.Infof("%s,%s,%s", "LSub", path, mailbox)
+	return nil, nil
+}
+
+func (a action) Status(session *goimap.Session, mailbox, category string) (string, error) {
+	log.Infof("%s,%s,%s", "Status", mailbox, category)
+	return "", nil
+}
+
+func (a action) Check(session *goimap.Session) error {
+	log.Infof("%s", "Check")
+	return nil
+}
+
+func (a action) Search(session *goimap.Session, keyword, criteria string) (string, error) {
+	log.Infof("%s,%s,%s", "Search", keyword, criteria)
+	return "", nil
+}
+
+func (a action) Copy(session *goimap.Session, mailId, mailBoxName string) error {
+	log.Infof("%s,%s,%s", "Copy", mailId, mailBoxName)
+	return nil
+}
+
+func (a action) CapaBility(session *goimap.Session) ([]string, error) {
+	log.Infof("%s", "CapaBility")
+	return nil, nil
+}
+
+func (a action) Noop(session *goimap.Session) error {
+	log.Infof("%s", "Noop")
+	return nil
+}
+
+func (a action) Login(session *goimap.Session, username, password string) error {
+	log.Infof("%s,%s,%s", "Login", username, password)
+	return nil
+}
+
+func (a action) Logout(session *goimap.Session) error {
+	log.Infof("%s", "Logout")
+	return nil
+}
+
+func (a action) Custom(session *goimap.Session, cmd string, args []string) ([]string, error) {
+	log.Infof("%s,%+v", cmd, args)
+	return nil, nil
+}

+ 57 - 0
server/listen/imap_server/goimap/action.go

@@ -0,0 +1,57 @@
+package goimap
+
+type Action interface {
+	Create(session *Session, path string) error                         // 创建邮箱
+	Delete(session *Session, path string) error                         // 删除邮箱
+	Rename(session *Session, oldPath, newPath string) error             // 重命名邮箱
+	List(session *Session, basePath, template string) ([]string, error) // 浏览邮箱
+	Append(session *Session, item string) error                         // 上传邮件
+	Select(session *Session, path string) error                         // 选择邮箱
+	/*
+		读取邮件的文本信息,且仅用于显示的目的。
+			ALL:只返回按照一定格式的邮件摘要,包括邮件标志、RFC822.SIZE、自身的时间和信封信息。IMAP客户机能够将标准邮件解析成这些信息并显示出来。
+			BODY:只返回邮件体文本格式和大小的摘要信息。IMAP客户机可以识别这些细腻,并向用户显示详细的关于邮件的信息。其实是一些非扩展的BODYSTRUCTURE的信息。
+			FAST:只返回邮件的一些摘要,包括邮件标志、RFC822.SIZE、和自身的时间。
+			FULL:同样的还是一些摘要信息,包括邮件标志、RFC822.SIZE、自身的时间和BODYSTRUCTURE的信息。
+			BODYSTRUCTUR:是邮件的[MIME-IMB]的体结构。这是服务器通过解析[RFC-2822]头中的[MIME-IMB]各字段和[MIME-IMB]头信息得出来的。包括的内容有:邮件正文的类型、字符集、编码方式等和各附件的类型、字符集、编码方式、文件名称等等。
+			ENVELOPE:信息的信封结构。是服务器通过解析[RFC-2822]头中的[MIME-IMB]各字段得出来的,默认各字段都是需要的。主要包括:自身的时间、附件数、收件人、发件人等。
+			FLAGS:此邮件的标志。
+			INTERNALDATE:自身的时间。
+			RFC822.SIZE:邮件的[RFC-2822]大小
+			RFC822.HEADER:在功能上等同于BODY.PEEK[HEADER],
+			RFC822:功能上等同于BODY[]。
+			RFC822.TEXT:功能上等同于BODY[TEXT]
+			UID:返回邮件的UID号,UID号是唯一标识邮件的一个号码。
+			BODY[section] <<partial>>:返回邮件的中的某一指定部分,返回的部分用section来表示,section部分包含的信息通常是代表某一部分的一个数字或者是下面的某一个部分:HEADER, HEADER.FIELDS, HEADER.FIELDS.NOT, MIME, and TEXT。如果section部分是空的话,那就代表返回全部的信息,包括头信息。
+			BODY[HEADER]返回完整的文件头信息。
+			BODY[HEADER.FIELDS ()]:在小括号里面可以指定返回的特定字段。
+			BODY[HEADER.FIELDS.NOT ()]:在小括号里面可以指定不需要返回的特定字段。
+			BODY[MIME]:返回邮件的[MIME-IMB]的头信息,在正常情况下跟BODY[HEADER]没有区别。
+			BODY[TEXT]:返回整个邮件体,这里的邮件体并不包括邮件头。
+			**/
+	Fetch(session *Session, mailIds, dataNames string) (string, error)
+	Store(session *Session, mailId, flags string) error            // STORE 命令用于修改指定邮件的属性,包括给邮件打上已读标记、删除标记
+	Close(session *Session) error                                  // 关闭文件夹
+	Expunge(session *Session) error                                // 删除已经标记为删除的邮件,释放服务器上的存储空间
+	Examine(session *Session, path string) error                   // 只读方式打开邮箱
+	Subscribe(session *Session, path string) error                 // 活动邮箱列表中增加一个邮箱
+	UnSubscribe(session *Session, path string) error               // 活动邮箱列表中去掉一个邮箱
+	LSub(session *Session, path, mailbox string) ([]string, error) // 显示那些使用SUBSCRIBE命令设置为活动邮箱的文件
+	/*
+		@category:
+			MESSAGES	邮箱中的邮件总数
+			RECENT	邮箱中标志为\RECENT的邮件数
+			UIDNEXT	可以分配给新邮件的下一个UID
+			UIDVALIDITY	邮箱的UID有效性标志
+			UNSEEN	邮箱中没有被标志为\UNSEEN的邮件数
+	*/
+	Status(session *Session, mailbox, category string) (string, error) // 查询邮箱的当前状态
+	Check(session *Session) error                                      // sync数据
+	Search(session *Session, keyword, criteria string) (string, error) // 命令可以根据搜索条件在处于活动状态的邮箱中搜索邮件,然后显示匹配的邮件编号
+	Copy(session *Session, mailId, mailBoxName string) error           // 把邮件从一个邮箱复制到另一个邮箱
+	CapaBility(session *Session) ([]string, error)                     // 返回IMAP服务器支持的功能列表
+	Noop(session *Session) error                                       // 什么都不做,连接保活
+	Login(session *Session, username, password string) error           // 登录
+	Logout(session *Session) error                                     // 注销登录
+	Custom(session *Session, cmd string, args []string) ([]string, error)
+}

+ 395 - 0
server/listen/imap_server/goimap/imap.go

@@ -0,0 +1,395 @@
+package goimap
+
+import (
+	"bufio"
+	"crypto/tls"
+	"errors"
+	"fmt"
+	"io"
+	"log/slog"
+	"net"
+	"strings"
+	"sync"
+	"time"
+)
+
+var (
+	eol = "\r\n"
+)
+
+// Server Imap服务实例
+type Server struct {
+	Domain           string        // 域名
+	Port             int           // 端口
+	TlsEnabled       bool          //是否启用Tls
+	TlsConfig        *tls.Config   // tls配置
+	ConnectAliveTime time.Duration // 连接存活时间,默认不超时
+	Action           Action
+	stop             chan bool
+	close            bool
+	lck              sync.Mutex
+}
+
+// NewImapServer 新建一个服务实例
+func NewImapServer(port int, domain string, tlsEnabled bool, tlsConfig *tls.Config, action Action) *Server {
+	return &Server{
+		Domain:     domain,
+		Port:       port,
+		TlsEnabled: tlsEnabled,
+		TlsConfig:  tlsConfig,
+		Action:     action,
+		stop:       make(chan bool, 1),
+	}
+}
+
+// Start 启动服务
+func (s *Server) Start() error {
+	if !s.TlsEnabled {
+		return s.startWithoutTLS()
+	} else {
+		return s.startWithTLS()
+	}
+}
+
+func (s *Server) startWithTLS() error {
+	if s.lck.TryLock() {
+		listener, err := tls.Listen("tcp", fmt.Sprintf(":%d", s.Port), s.TlsConfig)
+		if err != nil {
+			return err
+		}
+		s.close = false
+		defer func() {
+			listener.Close()
+		}()
+
+		go func() {
+			for {
+				conn, err := listener.Accept()
+				if err != nil {
+					if s.close {
+						break
+					} else {
+						continue
+					}
+				}
+				go s.handleClient(conn)
+			}
+		}()
+		<-s.stop
+	} else {
+		return errors.New("Server Is Running")
+	}
+
+	return nil
+}
+
+func (s *Server) startWithoutTLS() error {
+	if s.lck.TryLock() {
+		listener, err := net.Listen("tcp", fmt.Sprintf(":%d", s.Port))
+		if err != nil {
+			return err
+		}
+		s.close = false
+		defer func() {
+			listener.Close()
+		}()
+
+		go func() {
+			for {
+				conn, err := listener.Accept()
+				if err != nil {
+					if s.close {
+						break
+					} else {
+						continue
+					}
+				}
+				go s.handleClient(conn)
+			}
+		}()
+		<-s.stop
+	} else {
+		return errors.New("Server Is Running")
+	}
+
+	return nil
+}
+
+// Stop 停止服务
+func (s *Server) Stop() {
+	s.close = true
+	s.stop <- true
+}
+
+func (s *Server) handleClient(conn net.Conn) {
+	slog.Debug("Imap conn")
+
+	defer conn.Close()
+
+	session := &Session{
+		Conn:      conn,
+		AliveTime: time.Now(),
+	}
+	if s.TlsEnabled && s.TlsConfig != nil {
+		session.InTls = true
+	}
+
+	// 检查连接是否超时
+	if s.ConnectAliveTime != 0 {
+		go func() {
+			for {
+				if time.Now().Sub(session.AliveTime) >= s.ConnectAliveTime {
+					session.Conn.Close()
+					return
+				}
+				time.Sleep(3 * time.Second)
+			}
+		}()
+	}
+
+	reader := bufio.NewReader(conn)
+
+	fmt.Fprintf(conn, "* OK %s Imap Server powered by goimap"+eol, s.Domain)
+
+	for {
+		rawLine, err := reader.ReadString('\n')
+		if err != nil {
+			conn.Close()
+			return
+		}
+		session.AliveTime = time.Now()
+
+		nub, cmd, args := getCommand(rawLine)
+		slog.Debug(fmt.Sprintf("nub:%s cmd:%s args:%v", nub, cmd, args))
+
+		switch cmd {
+		case "CAPABILITY":
+			commands, err := s.Action.CapaBility(session)
+			if err != nil {
+				fmt.Fprintf(conn, "* BAD %s%s", err.Error(), eol)
+			} else {
+				ret := fmt.Sprintf("%s ", nub)
+				for _, command := range commands {
+					ret += fmt.Sprintf("%s%s", command, " ")
+				}
+				ret += fmt.Sprintf("%s", eol)
+				fmt.Fprintf(conn, ret)
+			}
+
+		case "CREATE":
+			if len(args) != 1 {
+				paramsErr(conn, "RENAME")
+				break
+			}
+			err := s.Action.Create(session, args[0])
+			output(conn, nub, err)
+		case "DELETE":
+			if len(args) != 1 {
+				paramsErr(conn, "RENAME")
+				break
+			}
+			err := s.Action.Delete(session, args[0])
+			output(conn, nub, err)
+		case "RENAME":
+			if len(args) != 2 {
+				paramsErr(conn, "RENAME")
+			} else {
+				err := s.Action.Rename(session, args[0], args[1])
+				output(conn, nub, err)
+			}
+		case "LIST":
+			if len(args) != 2 {
+				paramsErr(conn, "RENAME")
+			} else {
+				rets, err := s.Action.List(session, args[0], args[1])
+				if err != nil {
+					showBad(conn, err)
+				} else {
+					ret := ""
+					for _, str := range rets {
+						ret += str + eol
+					}
+					fmt.Fprintf(conn, ret)
+					showSucc(conn, nub)
+				}
+			}
+		case "APPEND":
+			slog.Debug("Append %s", args)
+		case "SELECT":
+			if len(args) != 1 {
+				paramsErr(conn, "RENAME")
+			} else {
+				err := s.Action.Select(session, args[0])
+				output(conn, nub, err)
+			}
+		case "FETCH":
+			if len(args) != 2 {
+				paramsErr(conn, "RENAME")
+			} else {
+				ret, err := s.Action.Fetch(session, args[0], args[1])
+				if err != nil {
+					showBad(conn, err)
+				} else {
+					fmt.Fprintf(conn, ret)
+					showSucc(conn, ret)
+				}
+			}
+		case "STORE":
+			if len(args) != 2 {
+				paramsErr(conn, "RENAME")
+			} else {
+				err := s.Action.Store(session, args[0], args[1])
+				output(conn, nub, err)
+			}
+		case "CLOSE":
+			err := s.Action.Close(session)
+			output(conn, nub, err)
+		case "EXPUNGE":
+			err := s.Action.Expunge(session)
+			output(conn, nub, err)
+		case "EXAMINE":
+			if len(args) != 1 {
+				paramsErr(conn, "EXAMINE")
+			}
+			err := s.Action.Examine(session, args[0])
+			output(conn, nub, err)
+		case "SUBSCRIBE":
+			if len(args) != 1 {
+				paramsErr(conn, "SUBSCRIBE")
+			} else {
+				err := s.Action.Subscribe(session, args[0])
+				output(conn, nub, err)
+			}
+		case "UNSUBSCRIBE":
+			if len(args) != 1 {
+				paramsErr(conn, "UNSUBSCRIBE")
+			} else {
+				err := s.Action.UnSubscribe(session, args[0])
+				output(conn, nub, err)
+			}
+		case "LSUB":
+			if len(args) != 2 {
+				paramsErr(conn, "LSUB")
+			} else {
+				rets, err := s.Action.LSub(session, args[0], args[1])
+				if err != nil {
+					showBad(conn, err)
+				} else {
+					ret := ""
+					for _, str := range rets {
+						ret += str + eol
+					}
+					fmt.Fprintf(conn, ret)
+					showSucc(conn, nub)
+				}
+			}
+		case "STATUS":
+			if len(args) != 2 {
+				paramsErr(conn, "STATUS")
+			} else {
+				ret, err := s.Action.Status(session, args[0], args[1])
+				if err != nil {
+					showBad(conn, err)
+				} else {
+					fmt.Fprintf(conn, ret)
+					showSucc(conn, nub)
+				}
+			}
+		case "CHECK":
+			err := s.Action.Check(session)
+			output(conn, nub, err)
+		case "SEARCH":
+			if len(args) < 2 {
+				paramsErr(conn, "SEARCH")
+			} else {
+				ret, err := s.Action.Search(session, args[0], args[1])
+				if err != nil {
+					showBad(conn, err)
+				} else {
+					fmt.Fprintf(conn, ret)
+					showSucc(conn, nub)
+				}
+			}
+		case "COPY":
+			if len(args) != 2 {
+				paramsErr(conn, "COPY")
+			} else {
+				err := s.Action.Copy(session, args[0], args[1])
+				output(conn, nub, err)
+			}
+
+		case "NOOP":
+			err := s.Action.Noop(session)
+			output(conn, nub, err)
+		case "LOGIN":
+			if len(args) != 2 {
+				paramsErr(conn, "LOGIN")
+			} else {
+				err := s.Action.Login(session, args[0], args[1])
+				output(conn, nub, err)
+			}
+		case "LOGOUT":
+			err := s.Action.Logout(session)
+			output(conn, nub, err)
+			conn.Close()
+		default:
+			rets, err := s.Action.Custom(session, cmd, args)
+			if err != nil {
+				fmt.Fprintf(conn, "* BAD %s %s", err.Error(), eol)
+			} else {
+				if len(rets) == 0 {
+					fmt.Fprintf(conn, "%s OK %s", nub, eol)
+				} else if len(rets) == 1 {
+					fmt.Fprintf(conn, "%s OK %s%s", nub, rets[0], eol)
+				} else {
+					ret := fmt.Sprintf("%s OK %s", nub, eol)
+					for _, re := range rets {
+						ret += fmt.Sprintf("%s%s", re, eol)
+					}
+					ret += "." + eol
+					fmt.Fprintf(conn, ret)
+				}
+			}
+		}
+
+	}
+}
+
+// cuts the line into command and arguments
+func getCommand(line string) (string, string, []string) {
+	line = strings.Trim(line, "\r \n")
+	cmd := strings.Split(line, " ")
+
+	return strings.ToTitle(cmd[0]), strings.ToTitle(cmd[1]), cmd[2:]
+}
+
+func getSafeArg(args []string, nr int) string {
+	if nr < len(args) {
+		return args[nr]
+	}
+	return ""
+}
+
+func showSucc(w io.Writer, nub string) {
+	fmt.Fprintf(w, "%s OK success %s", nub, eol)
+}
+
+func showBad(w io.Writer, err error) {
+	if err == nil {
+		fmt.Fprintf(w, "* BAD %s", eol)
+		return
+	}
+	fmt.Fprintf(w, "* BAD %s%s", err.Error(), eol)
+}
+
+func output(w io.Writer, nub string, err error) {
+	if err != nil {
+		showSucc(w, nub)
+	} else {
+		showBad(w, err)
+	}
+}
+
+func paramsErr(w io.Writer, commend string) {
+	fmt.Fprintf(w, "* BAD %s parameters! %s", commend, eol)
+}

+ 26 - 0
server/listen/imap_server/goimap/session.go

@@ -0,0 +1,26 @@
+package goimap
+
+import (
+	"context"
+	"net"
+	"time"
+)
+
+type Status int8
+
+const (
+	UNAUTHORIZED Status = 1
+	TRANSACTION  Status = 2
+	UPDATE       Status = 3
+)
+
+type Session struct {
+	Status      Status
+	User        string
+	DeleteIds   []int64
+	Ctx         context.Context
+	Conn        net.Conn
+	InTls       bool
+	AliveTime   time.Time
+	CurrentPath string //当前选择的文件夹
+}

+ 39 - 0
server/listen/imap_server/imap_server.go

@@ -0,0 +1,39 @@
+package imap_server
+
+import (
+	"crypto/rand"
+	"crypto/tls"
+	"github.com/Jinnrry/pmail/config"
+	"github.com/Jinnrry/pmail/listen/imap_server/goimap"
+	log "github.com/sirupsen/logrus"
+	"time"
+)
+
+var instanceTLS *goimap.Server
+
+// StarTLS 启动TLS端口监听,不加密的代码就懒得写了
+func StarTLS() {
+	crt, err := tls.LoadX509KeyPair(config.Instance.SSLPublicKeyPath, config.Instance.SSLPrivateKeyPath)
+	if err != nil {
+		panic(err)
+	}
+	tlsConfig := &tls.Config{}
+	tlsConfig.Certificates = []tls.Certificate{crt}
+	tlsConfig.Time = time.Now
+	tlsConfig.Rand = rand.Reader
+	instanceTLS = goimap.NewImapServer(993, "imap."+config.Instance.Domain, true, tlsConfig, action{})
+	instanceTLS.ConnectAliveTime = 5 * time.Minute
+
+	log.Infof("IMAP With TLS Server Start On Port :993")
+
+	err = instanceTLS.Start()
+	if err != nil {
+		panic(err)
+	}
+}
+
+func Stop() {
+	if instanceTLS != nil {
+		instanceTLS.Stop()
+	}
+}

+ 0 - 0
server/pop3_server/action.go → server/listen/pop3_server/action.go


+ 55 - 0
server/listen/pop3_server/action_test.go

@@ -0,0 +1,55 @@
+package pop3_server
+
+import (
+	"bytes"
+	"fmt"
+	"github.com/Jinnrry/gopop"
+	"github.com/Jinnrry/pmail/config"
+	"github.com/Jinnrry/pmail/db"
+	"github.com/Jinnrry/pmail/utils/context"
+	"github.com/emersion/go-message/mail"
+	"io"
+	"testing"
+)
+
+func Test_action_Retr(t *testing.T) {
+	config.Init()
+	db.Init("")
+
+	a := action{}
+	session := &gopop.Session{
+		Ctx: &context.Context{
+			UserID: 1,
+		},
+	}
+	got, got1, err := a.Retr(session, 301)
+
+	_, _, _ = got, got1, err
+}
+
+func Test_email(t *testing.T) {
+	var b bytes.Buffer
+
+	// Create our mail header
+	var h mail.Header
+
+	// Create a new mail writer
+	mw, _ := mail.CreateWriter(&b, h)
+
+	// Create a text part
+	tw, _ := mw.CreateInline()
+
+	var html mail.InlineHeader
+
+	html.Header.Set("Content-Transfer-Encoding", "base64")
+	w, _ := tw.CreatePart(html)
+
+	io.WriteString(w, "=")
+
+	w.Close()
+
+	tw.Close()
+
+	fmt.Printf("%s", b.String())
+
+}

+ 0 - 0
server/pop3_server/pop3server.go → server/listen/pop3_server/pop3server.go


+ 0 - 0
server/smtp_server/read_content.go → server/listen/smtp_server/read_content.go


+ 0 - 0
server/smtp_server/read_content_test.go → server/listen/smtp_server/read_content_test.go


+ 0 - 0
server/smtp_server/smtp.go → server/listen/smtp_server/smtp.go


+ 0 - 0
server/smtp_server/smtp_test/sendEmailTest.py → server/listen/smtp_server/smtp_test/sendEmailTest.py


+ 1 - 1
server/main.go

@@ -4,7 +4,7 @@ import (
 	"bytes"
 	"fmt"
 	"github.com/Jinnrry/pmail/config"
-	"github.com/Jinnrry/pmail/cron_server"
+	"github.com/Jinnrry/pmail/listen/cron_server"
 	"github.com/Jinnrry/pmail/res_init"
 	"github.com/Jinnrry/pmail/utils/context"
 	log "github.com/sirupsen/logrus"

+ 8 - 3
server/res_init/init.go

@@ -6,12 +6,13 @@ import (
 	"github.com/Jinnrry/pmail/db"
 	"github.com/Jinnrry/pmail/dto/parsemail"
 	"github.com/Jinnrry/pmail/hooks"
-	"github.com/Jinnrry/pmail/http_server"
-	"github.com/Jinnrry/pmail/pop3_server"
+	"github.com/Jinnrry/pmail/listen/http_server"
+	"github.com/Jinnrry/pmail/listen/imap_server"
+	"github.com/Jinnrry/pmail/listen/pop3_server"
+	"github.com/Jinnrry/pmail/listen/smtp_server"
 	"github.com/Jinnrry/pmail/services/setup/ssl"
 	"github.com/Jinnrry/pmail/session"
 	"github.com/Jinnrry/pmail/signal"
-	"github.com/Jinnrry/pmail/smtp_server"
 	"github.com/Jinnrry/pmail/utils/file"
 	log "github.com/sirupsen/logrus"
 	"os"
@@ -47,6 +48,8 @@ func Init(serverVersion string) {
 		// pop3 server start
 		go pop3_server.Start()
 		go pop3_server.StartWithTls()
+		// imap server start
+		go imap_server.StarTLS()
 
 		configStr, _ := json.Marshal(config.Instance)
 		log.Warnf("Config File Info:  %s", configStr)
@@ -58,6 +61,7 @@ func Init(serverVersion string) {
 			http_server.HttpsStop()
 			http_server.HttpStop()
 			pop3_server.Stop()
+			imap_server.Stop()
 			hooks.Stop()
 		case <-signal.StopChan:
 			log.Infof("Server Stop!")
@@ -65,6 +69,7 @@ func Init(serverVersion string) {
 			http_server.HttpsStop()
 			http_server.HttpStop()
 			pop3_server.Stop()
+			imap_server.Stop()
 			hooks.Stop()
 			return
 		}