Smail/server/hooks/framework/framework.go
Jinnrry e6a56b199d
v2.6.2 (#177)
1、修复邮件插入失败
2、插件支持设置页面
3、修复pop3邮箱拉取权限判断
4、修复非管理员账户的附件权限判断

Co-authored-by: jinnrry <i@jinnrry.com>
2024-07-27 20:47:42 +08:00

228 lines
6.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package framework
import (
"bytes"
"encoding/json"
"fmt"
"github.com/Jinnrry/pmail/dto/parsemail"
"github.com/Jinnrry/pmail/models"
"github.com/Jinnrry/pmail/utils/context"
log "github.com/sirupsen/logrus"
"io"
"net"
"net/http"
"os"
"path/filepath"
"strings"
"time"
)
type EmailHook interface {
// SendBefore 邮件发送前的数据 同步执行
SendBefore(ctx *context.Context, email *parsemail.Email)
// SendAfter 邮件发送后的数据,err是每个收信服务器的错误信息 异步执行
SendAfter(ctx *context.Context, email *parsemail.Email, err map[string]error)
// ReceiveParseBefore 接收到邮件,解析之前的原始数据 同步执行
ReceiveParseBefore(ctx *context.Context, email *[]byte)
// ReceiveParseAfter 接收到邮件,解析之后的结构化数据 (收信规则前,写数据库前执行) 同步执行
ReceiveParseAfter(ctx *context.Context, email *parsemail.Email)
// ReceiveSaveAfter 邮件落库以后执行(收信规则后执行) 异步执行
ReceiveSaveAfter(ctx *context.Context, email *parsemail.Email, ue []*models.UserEmail)
// GetName 获取插件名称
GetName(ctx *context.Context) string
// SettingsHtml 插件页面
SettingsHtml(ctx *context.Context, url string, requestData string) string
}
// HookDTO PMail 主程序和插件通信的结构体
type HookDTO struct {
ServerVersion string // 服务端程序版本
Ctx *context.Context // 上下文
Email *parsemail.Email // 邮件内容
EmailByte *[]byte // 未解析前的文件内容
ErrMap map[string]error // 错误信息
UserEmail []*models.UserEmail
}
type SettingsHtmlRequest struct {
Ctx *context.Context // 上下文
URL string
RequestData string
}
type Plugin struct {
name string
hook EmailHook
}
func CreatePlugin(name string, hook EmailHook) *Plugin {
return &Plugin{
name: name,
hook: hook,
}
}
type logFormatter struct {
}
// Format 定义日志输出格式
func (l *logFormatter) Format(entry *log.Entry) ([]byte, error) {
b := bytes.Buffer{}
b.WriteString(fmt.Sprintf("[%s]", entry.Level.String()))
b.WriteString(fmt.Sprintf("[%s]", entry.Time.Format("2006-01-02 15:04:05")))
if entry.Context != nil {
ctx := entry.Context.(*context.Context)
if ctx != nil {
b.WriteString(fmt.Sprintf("[%s]", ctx.GetValue(context.LogID)))
}
}
b.WriteString(fmt.Sprintf("[%s:%d]", entry.Caller.File, entry.Caller.Line))
b.WriteString(entry.Message)
b.WriteString("\n")
return b.Bytes(), nil
}
func (p *Plugin) Run() {
// 设置日志格式为json格式
log.SetFormatter(&logFormatter{})
log.SetReportCaller(true)
// 设置将日志输出到标准输出(默认的输出为stderr,标准错误)
// 日志消息输出可以是任意的io.writer类型
log.SetOutput(os.Stdout)
if len(os.Args) < 2 {
panic("Command Params Error!")
}
mux := http.NewServeMux()
mux.HandleFunc("/SendBefore", func(writer http.ResponseWriter, request *http.Request) {
log.Debugf("[%s] SendBefore Start", p.name)
var hookDTO HookDTO
body, _ := io.ReadAll(request.Body)
err := json.Unmarshal(body, &hookDTO)
if err != nil {
log.Errorf("params error %+v", err)
return
}
p.hook.SendBefore(hookDTO.Ctx, hookDTO.Email)
body, _ = json.Marshal(hookDTO)
writer.Write(body)
log.Debugf("[%s] SendBefore End", p.name)
})
mux.HandleFunc("/SendAfter", func(writer http.ResponseWriter, request *http.Request) {
log.Debugf("[%s] SendAfter Start", p.name)
var hookDTO HookDTO
body, _ := io.ReadAll(request.Body)
err := json.Unmarshal(body, &hookDTO)
if err != nil {
log.Errorf("params error %+v", err)
return
}
p.hook.SendAfter(hookDTO.Ctx, hookDTO.Email, hookDTO.ErrMap)
body, _ = json.Marshal(hookDTO)
writer.Write(body)
log.Debugf("[%s] SendAfter End", p.name)
})
mux.HandleFunc("/ReceiveParseBefore", func(writer http.ResponseWriter, request *http.Request) {
log.Debugf("[%s] ReceiveParseBefore Start", p.name)
var hookDTO HookDTO
body, _ := io.ReadAll(request.Body)
err := json.Unmarshal(body, &hookDTO)
if err != nil {
log.Errorf("params error %+v", err)
return
}
p.hook.ReceiveParseBefore(hookDTO.Ctx, hookDTO.EmailByte)
body, _ = json.Marshal(hookDTO)
writer.Write(body)
log.Debugf("[%s] ReceiveParseBefore End", p.name)
})
mux.HandleFunc("/ReceiveParseAfter", func(writer http.ResponseWriter, request *http.Request) {
log.Debugf("[%s] ReceiveParseAfter Start", p.name)
var hookDTO HookDTO
body, _ := io.ReadAll(request.Body)
err := json.Unmarshal(body, &hookDTO)
if err != nil {
log.Errorf("params error %+v", err)
return
}
p.hook.ReceiveParseAfter(hookDTO.Ctx, hookDTO.Email)
body, _ = json.Marshal(hookDTO)
writer.Write(body)
log.Debugf("[%s] ReceiveParseAfter End", p.name)
})
mux.HandleFunc("/ReceiveSaveAfter", func(writer http.ResponseWriter, request *http.Request) {
log.Debugf("[%s] ReceiveSaveAfter Start", p.name)
var hookDTO HookDTO
body, _ := io.ReadAll(request.Body)
err := json.Unmarshal(body, &hookDTO)
if err != nil {
log.Errorf("params error %+v", err)
return
}
p.hook.ReceiveSaveAfter(hookDTO.Ctx, hookDTO.Email, hookDTO.UserEmail)
body, _ = json.Marshal(hookDTO)
writer.Write(body)
log.Debugf("[%s] ReceiveSaveAfter End", p.name)
})
mux.HandleFunc("/GetName", func(writer http.ResponseWriter, request *http.Request) {
log.Debugf("[%s] GetName Start", p.name)
var hookDTO HookDTO
body, _ := io.ReadAll(request.Body)
err := json.Unmarshal(body, &hookDTO)
if err != nil {
log.Errorf("params error %+v", err)
return
}
name := strings.ReplaceAll(p.hook.GetName(hookDTO.Ctx), " ", "")
writer.Write([]byte(name))
log.Debugf("[%s] GetName End", p.name)
})
mux.HandleFunc("/SettingsHtml", func(writer http.ResponseWriter, request *http.Request) {
log.Debugf("[%s] SettingsHtml Start", p.name)
var hookDTO SettingsHtmlRequest
body, _ := io.ReadAll(request.Body)
err := json.Unmarshal(body, &hookDTO)
if err != nil {
log.Errorf("params error %+v", err)
return
}
html := p.hook.SettingsHtml(hookDTO.Ctx, hookDTO.URL, hookDTO.RequestData)
writer.Write([]byte(html))
log.Debugf("[%s] SettingsHtml End", p.name)
})
server := http.Server{
ReadTimeout: 5 * time.Second,
WriteTimeout: 5 * time.Second,
Handler: mux,
}
filePath := getExePath() + "/" + os.Args[1]
unixListener, err := net.Listen("unix", filePath)
if err != nil {
panic(err)
}
err = server.Serve(unixListener)
if err != nil {
panic(err)
}
}
func getExePath() string {
//return "/PMail/server/hooks/spam_block" //debug socket path
ex, err := os.Executable()
if err != nil {
panic(err)
}
exePath := filepath.Dir(ex)
return exePath
}