framework.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. package framework
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "github.com/Jinnrry/pmail/dto/parsemail"
  7. "github.com/Jinnrry/pmail/models"
  8. "github.com/Jinnrry/pmail/utils/context"
  9. log "github.com/sirupsen/logrus"
  10. "io"
  11. "net"
  12. "net/http"
  13. "os"
  14. "path/filepath"
  15. "strings"
  16. "time"
  17. )
  18. type EmailHook interface {
  19. // SendBefore 邮件发送前的数据 同步执行
  20. SendBefore(ctx *context.Context, email *parsemail.Email)
  21. // SendAfter 邮件发送后的数据,err是每个收信服务器的错误信息 异步执行
  22. SendAfter(ctx *context.Context, email *parsemail.Email, err map[string]error)
  23. // ReceiveParseBefore 接收到邮件,解析之前的原始数据 同步执行
  24. ReceiveParseBefore(ctx *context.Context, email *[]byte)
  25. // ReceiveParseAfter 接收到邮件,解析之后的结构化数据 (收信规则前,写数据库前执行) 同步执行
  26. ReceiveParseAfter(ctx *context.Context, email *parsemail.Email)
  27. // ReceiveSaveAfter 邮件落库以后执行(收信规则后执行) 异步执行
  28. ReceiveSaveAfter(ctx *context.Context, email *parsemail.Email, ue []*models.UserEmail)
  29. // GetName 获取插件名称
  30. GetName(ctx *context.Context) string
  31. // SettingsHtml 插件页面
  32. SettingsHtml(ctx *context.Context, url string, requestData string) string
  33. }
  34. // HookDTO PMail 主程序和插件通信的结构体
  35. type HookDTO struct {
  36. ServerVersion string // 服务端程序版本
  37. Ctx *context.Context // 上下文
  38. Email *parsemail.Email // 邮件内容
  39. EmailByte *[]byte // 未解析前的文件内容
  40. ErrMap map[string]error // 错误信息
  41. UserEmail []*models.UserEmail
  42. }
  43. type SettingsHtmlRequest struct {
  44. Ctx *context.Context // 上下文
  45. URL string
  46. RequestData string
  47. }
  48. type Plugin struct {
  49. name string
  50. hook EmailHook
  51. }
  52. func CreatePlugin(name string, hook EmailHook) *Plugin {
  53. return &Plugin{
  54. name: name,
  55. hook: hook,
  56. }
  57. }
  58. type logFormatter struct {
  59. }
  60. // Format 定义日志输出格式
  61. func (l *logFormatter) Format(entry *log.Entry) ([]byte, error) {
  62. b := bytes.Buffer{}
  63. b.WriteString(fmt.Sprintf("[%s]", entry.Level.String()))
  64. b.WriteString(fmt.Sprintf("[%s]", entry.Time.Format("2006-01-02 15:04:05")))
  65. if entry.Context != nil {
  66. ctx := entry.Context.(*context.Context)
  67. if ctx != nil {
  68. b.WriteString(fmt.Sprintf("[%s]", ctx.GetValue(context.LogID)))
  69. }
  70. }
  71. b.WriteString(fmt.Sprintf("[%s:%d]", entry.Caller.File, entry.Caller.Line))
  72. b.WriteString(entry.Message)
  73. b.WriteString("\n")
  74. return b.Bytes(), nil
  75. }
  76. func (p *Plugin) Run() {
  77. // 设置日志格式为json格式
  78. log.SetFormatter(&logFormatter{})
  79. log.SetReportCaller(true)
  80. // 设置将日志输出到标准输出(默认的输出为stderr,标准错误)
  81. // 日志消息输出可以是任意的io.writer类型
  82. log.SetOutput(os.Stdout)
  83. if len(os.Args) < 2 {
  84. panic("Command Params Error!")
  85. }
  86. mux := http.NewServeMux()
  87. mux.HandleFunc("/SendBefore", func(writer http.ResponseWriter, request *http.Request) {
  88. log.Debugf("[%s] SendBefore Start", p.name)
  89. var hookDTO HookDTO
  90. body, _ := io.ReadAll(request.Body)
  91. err := json.Unmarshal(body, &hookDTO)
  92. if err != nil {
  93. log.Errorf("params error %+v", err)
  94. return
  95. }
  96. p.hook.SendBefore(hookDTO.Ctx, hookDTO.Email)
  97. body, _ = json.Marshal(hookDTO)
  98. writer.Write(body)
  99. log.Debugf("[%s] SendBefore End", p.name)
  100. })
  101. mux.HandleFunc("/SendAfter", func(writer http.ResponseWriter, request *http.Request) {
  102. log.Debugf("[%s] SendAfter Start", p.name)
  103. var hookDTO HookDTO
  104. body, _ := io.ReadAll(request.Body)
  105. err := json.Unmarshal(body, &hookDTO)
  106. if err != nil {
  107. log.Errorf("params error %+v", err)
  108. return
  109. }
  110. p.hook.SendAfter(hookDTO.Ctx, hookDTO.Email, hookDTO.ErrMap)
  111. body, _ = json.Marshal(hookDTO)
  112. writer.Write(body)
  113. log.Debugf("[%s] SendAfter End", p.name)
  114. })
  115. mux.HandleFunc("/ReceiveParseBefore", func(writer http.ResponseWriter, request *http.Request) {
  116. log.Debugf("[%s] ReceiveParseBefore Start", p.name)
  117. var hookDTO HookDTO
  118. body, _ := io.ReadAll(request.Body)
  119. err := json.Unmarshal(body, &hookDTO)
  120. if err != nil {
  121. log.Errorf("params error %+v", err)
  122. return
  123. }
  124. p.hook.ReceiveParseBefore(hookDTO.Ctx, hookDTO.EmailByte)
  125. body, _ = json.Marshal(hookDTO)
  126. writer.Write(body)
  127. log.Debugf("[%s] ReceiveParseBefore End", p.name)
  128. })
  129. mux.HandleFunc("/ReceiveParseAfter", func(writer http.ResponseWriter, request *http.Request) {
  130. log.Debugf("[%s] ReceiveParseAfter Start", p.name)
  131. var hookDTO HookDTO
  132. body, _ := io.ReadAll(request.Body)
  133. err := json.Unmarshal(body, &hookDTO)
  134. if err != nil {
  135. log.Errorf("params error %+v", err)
  136. return
  137. }
  138. p.hook.ReceiveParseAfter(hookDTO.Ctx, hookDTO.Email)
  139. body, _ = json.Marshal(hookDTO)
  140. writer.Write(body)
  141. log.Debugf("[%s] ReceiveParseAfter End", p.name)
  142. })
  143. mux.HandleFunc("/ReceiveSaveAfter", func(writer http.ResponseWriter, request *http.Request) {
  144. log.Debugf("[%s] ReceiveSaveAfter Start", p.name)
  145. var hookDTO HookDTO
  146. body, _ := io.ReadAll(request.Body)
  147. err := json.Unmarshal(body, &hookDTO)
  148. if err != nil {
  149. log.Errorf("params error %+v", err)
  150. return
  151. }
  152. p.hook.ReceiveSaveAfter(hookDTO.Ctx, hookDTO.Email, hookDTO.UserEmail)
  153. body, _ = json.Marshal(hookDTO)
  154. writer.Write(body)
  155. log.Debugf("[%s] ReceiveSaveAfter End", p.name)
  156. })
  157. mux.HandleFunc("/GetName", func(writer http.ResponseWriter, request *http.Request) {
  158. log.Debugf("[%s] GetName Start", p.name)
  159. var hookDTO HookDTO
  160. body, _ := io.ReadAll(request.Body)
  161. err := json.Unmarshal(body, &hookDTO)
  162. if err != nil {
  163. log.Errorf("params error %+v", err)
  164. return
  165. }
  166. name := strings.ReplaceAll(p.hook.GetName(hookDTO.Ctx), " ", "")
  167. writer.Write([]byte(name))
  168. log.Debugf("[%s] GetName End", p.name)
  169. })
  170. mux.HandleFunc("/SettingsHtml", func(writer http.ResponseWriter, request *http.Request) {
  171. log.Debugf("[%s] SettingsHtml Start", p.name)
  172. var hookDTO SettingsHtmlRequest
  173. body, _ := io.ReadAll(request.Body)
  174. err := json.Unmarshal(body, &hookDTO)
  175. if err != nil {
  176. log.Errorf("params error %+v", err)
  177. return
  178. }
  179. html := p.hook.SettingsHtml(hookDTO.Ctx, hookDTO.URL, hookDTO.RequestData)
  180. writer.Write([]byte(html))
  181. log.Debugf("[%s] SettingsHtml End", p.name)
  182. })
  183. server := http.Server{
  184. ReadTimeout: 5 * time.Second,
  185. WriteTimeout: 5 * time.Second,
  186. Handler: mux,
  187. }
  188. filePath := getExePath() + "/" + os.Args[1]
  189. unixListener, err := net.Listen("unix", filePath)
  190. if err != nil {
  191. panic(err)
  192. }
  193. err = server.Serve(unixListener)
  194. if err != nil {
  195. panic(err)
  196. }
  197. }
  198. func getExePath() string {
  199. //return "/PMail/server/hooks/spam_block" //debug socket path
  200. ex, err := os.Executable()
  201. if err != nil {
  202. panic(err)
  203. }
  204. exePath := filepath.Dir(ex)
  205. return exePath
  206. }