98
package main
import (
"bytes"
"encoding/base64"
"io"
"mime"
"mime/multipart"
"net/mail"
"net/textproto"
"regexp"
"strings"
)
type FileList map[string][]byte
func getfiles(files *FileList, parts interface{}) {
var body io.Reader
var head textproto.MIMEHeader
switch part := parts.(type) {
case *mail.Message:
body = part.Body
head = textproto.MIMEHeader(part.Header)
case *multipart.Part:
body = part
head = part.Header
}
mediaType, params, _ := mime.ParseMediaType(head.Get("Content-Type"))
if strings.HasPrefix(mediaType, "multipart/") {
mr := multipart.NewReader(body, params["boundary"])
var part *multipart.Part
var err error
for {
if part, err = mr.NextPart(); err != nil {
break
}
getfiles(files, part)
part.Close()
}
return
}
content, _ := io.ReadAll(body)
if head.Get("Content-Transfer-Encoding") == "base64" {
content, _ = base64.StdEncoding.DecodeString(string(content))
}
if len(content) == 0 {
return
}
if mediaType == "text/html" || mediaType == "text/plain" {
content = []byte(func(s string) string {
s = strings.ReplaceAll(s, "=\n", "")
s = strings.ReplaceAll(s, "=3D", "=")
return s
}(string(content)))
}
name := "body.txt"
if params["name"] != "" {
name = params["name"]
}
if strings.Contains(mediaType, "html") {
name = "body.html"
}
(*files)[name] = append((*files)[name], content...)
if head.Get("Content-ID") != "" {
cid := head.Get("Content-ID")
cid = strings.TrimPrefix(cid, "<")
cid = strings.TrimSuffix(cid, ">")
cid = "cidname: " + cid + " " + name + "\n"
(*files)["header.txt"] = append((*files)["header.txt"], []byte(cid)...)
}
}
var cidheader = regexp.MustCompile(`^cidname: [^ ]+ [^ ]+$`) // Content-ID header
func EmlFiles(eml *mail.Message, head []byte) FileList {
files := make(FileList)
getfiles(&files, eml)
for _, v := range cidheader.FindAll(files["header.txt"], -1) {
v = bytes.TrimSuffix(v, []byte("\n"))
v = bytes.ReplaceAll(v, []byte("cidname: "), []byte("cid:"))
n := bytes.Index(v, []byte(" "))
files["body.html"] = bytes.ReplaceAll(files["body.html"], v[:n], v[n+1:])
}
files["header.txt"] = head
return files
}
func GetFiles(b *bytes.Buffer) (FileList, error) {
head := bytes.SplitN(b.Bytes(), []byte{10, 10}, 2)[0]
head = bytes.ReplaceAll(head, []byte{'\t'}, []byte{' '})
e, err := mail.ReadMessage(b)
if err != nil {
return nil, err
}
return EmlFiles(e, head), nil
}