Files
warmbly/internal/pkg/emsg/emsg.go
T
2026-01-17 14:11:14 +00:00

96 lines
1.9 KiB
Go

package emsg
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
)
const Magic = "EMSG"
const Version = 1
// Bitmask flags for sections
const (
FlagPlainText uint32 = 1 << 0
FlagHTMLBody uint32 = 1 << 1
)
// EmailBlob represents a binary-encoded email body and metadata.
type EmailBlob struct {
PlainText []byte
HTMLBody []byte
}
// EncodeBinary serializes the blob into binary format.
func (b *EmailBlob) EncodeBinary() ([]byte, error) {
var flags uint32
parts := make([][]byte, 0, 5)
if len(b.PlainText) > 0 {
flags |= FlagPlainText
parts = append(parts, b.PlainText)
}
if len(b.HTMLBody) > 0 {
flags |= FlagHTMLBody
parts = append(parts, b.HTMLBody)
}
buf := new(bytes.Buffer)
// Write header
buf.WriteString(Magic) // 4 bytes
buf.WriteByte(Version) // 1 byte
binary.Write(buf, binary.BigEndian, flags) // 4 bytes
// Write each section [len][data]
for _, p := range parts {
binary.Write(buf, binary.BigEndian, uint32(len(p)))
buf.Write(p)
}
return buf.Bytes(), nil
}
// DecodeBinary parses a binary blob into an EmailBlob struct.
func DecodeBinary(r io.Reader) (*EmailBlob, error) {
header := make([]byte, 9)
if _, err := io.ReadFull(r, header); err != nil {
return nil, err
}
if string(header[:4]) != Magic {
return nil, errors.New("invalid magic header")
}
version := header[4]
if version != Version {
return nil, fmt.Errorf("unsupported version: %d", version)
}
flags := binary.BigEndian.Uint32(header[5:9])
readSection := func() ([]byte, error) {
var length uint32
if err := binary.Read(r, binary.BigEndian, &length); err != nil {
return nil, err
}
data := make([]byte, length)
if _, err := io.ReadFull(r, data); err != nil {
return nil, err
}
return data, nil
}
b := &EmailBlob{}
if flags&FlagPlainText != 0 {
b.PlainText, _ = readSection()
}
if flags&FlagHTMLBody != 0 {
b.HTMLBody, _ = readSection()
}
return b, nil
}