Files
EMLy/backend/utils/mail/msg_reader.go
Flavio Fois 0cda0a26fc v1.2.2
2026-02-04 19:57:31 +01:00

149 lines
4.3 KiB
Go

package internal
import (
"encoding/base64"
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
)
// ReadMsgFile reads a .msg file using the native Go parser.
func ReadMsgFile(filePath string) (*EmailData, error) {
return ReadMsgPecFile(filePath)
}
func OSSReadMsgFile(filePath string) (*EmailData, error) {
return parseMsgFile(filePath)
}
// parseSignedMsgExec executes 'signed_msg.exe' via cmd to convert a MSG to JSON,
// then processes the output to reconstruct the PEC email data.
func ReadMsgPecFile(filePath string) (*EmailData, error) {
fmt.Println("Called!")
// 1. Locate signed_msg.exe
exePath, err := os.Executable()
if err != nil {
return nil, fmt.Errorf("failed to get executable path: %w", err)
}
baseDir := filepath.Dir(exePath)
helperExe := filepath.Join(baseDir, "signed_msg.exe")
fmt.Println(helperExe)
// 2. Create temp file for JSON output
// Using generic temp file naming with timestamp
timestamp := time.Now().Format("20060102_150405")
tempFile, err := os.CreateTemp("", fmt.Sprintf("pec_output_%s_*.json", timestamp))
if err != nil {
return nil, fmt.Errorf("failed to create temp file: %w", err)
}
tempPath := tempFile.Name()
tempFile.Close() // Close immediately, exe will write to it
// defer os.Remove(tempPath) // Cleanup
// 3. Run signed_msg.exe <msgPath> <jsonPath>
// Use exec.Command
// Note: Command might need to be "cmd", "/C", ... but usually direct execution works on Windows
fmt.Println(helperExe, filePath, tempPath)
cmd := exec.Command(helperExe, filePath, tempPath)
// Hide window?
// cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} // Requires syscall import
output, err := cmd.CombinedOutput()
if err != nil {
return nil, fmt.Errorf("signed_msg.exe failed: %s, output: %s", err, string(output))
}
// 4. Read JSON output
jsonData, err := os.ReadFile(tempPath)
if err != nil {
return nil, fmt.Errorf("failed to read json output: %w", err)
}
// 5. Parse JSON
var pecJson struct {
Subject string `json:"subject"`
From string `json:"from"`
To []string `json:"to"`
Cc []string `json:"cc"`
Bcc []string `json:"bcc"`
Body string `json:"body"`
HtmlBody string `json:"htmlBody"`
Attachments []struct {
Filename string `json:"filename"`
ContentType string `json:"contentType"`
Data string `json:"data"` // Base64
DataFormat string `json:"dataFormat"` // "base64" (optional)
} `json:"attachments"`
}
if err := json.Unmarshal(jsonData, &pecJson); err != nil {
return nil, fmt.Errorf("failed to parse json output: %w", err)
}
// 6. Check for postacert.eml to determine if it is a PEC
var foundPostacert bool
var hasDatiCert, hasSmime bool
// We'll prepare attachments listing at the same time
var attachments []EmailAttachment
for _, att := range pecJson.Attachments {
attData, err := base64.StdEncoding.DecodeString(att.Data)
if err != nil {
fmt.Printf("Failed to decode attachment %s: %v\n", att.Filename, err)
continue
}
filenameLower := strings.ToLower(att.Filename)
if filenameLower == "postacert.eml" {
foundPostacert = true
}
if filenameLower == "daticert.xml" {
hasDatiCert = true
}
if filenameLower == "smime.p7s" {
hasSmime = true
}
attachments = append(attachments, EmailAttachment{
Filename: att.Filename,
ContentType: att.ContentType,
Data: attData,
})
}
if !foundPostacert {
// Maybe its a normal MSG, continue to try to parse it as a regular email
normalMsgEmail, err := parseMsgFile(filePath)
if err != nil {
return nil, fmt.Errorf("failed to find postacert.eml and also failed to parse as normal MSG: %w", err)
}
return normalMsgEmail, nil
}
// 7. It is a PEC. Return the outer message (wrapper)
// so the user can see the PEC envelope and attachments (postacert.eml, etc.)
body := pecJson.HtmlBody
if body == "" {
body = pecJson.Body
}
return &EmailData{
From: convertToUTF8(pecJson.From),
To: pecJson.To, // Assuming format is already correct or compatible
Cc: pecJson.Cc,
Bcc: pecJson.Bcc,
Subject: convertToUTF8(pecJson.Subject),
Body: convertToUTF8(body),
Attachments: attachments,
IsPec: hasDatiCert || hasSmime, // Typical PEC indicators
HasInnerEmail: foundPostacert,
}, nil
}