- Comments explain what each function does and why - Fixed unused strconv import in queue.go - Better documentation for SMTP email service - Explained journalctl log parsing in logs.go - Clarified queue operations in queue.go
151 lines
4.9 KiB
Go
151 lines
4.9 KiB
Go
// Package mail provides functions for interacting with the mail server system.
|
|
// Currently supports Postfix mail queue operations via postqueue/postsuper commands.
|
|
package mail
|
|
|
|
import (
|
|
"bufio" // buffered scanner for reading line-by-line
|
|
"bytes" // buffer management
|
|
"encoding/json" // JSON parsing for postqueue output
|
|
"fmt" // formatted error messages
|
|
"os/exec" // executing external commands
|
|
"regexp" // regular expressions (for parsing queue output)
|
|
"strings" // string manipulation
|
|
)
|
|
|
|
// QueueEntry represents a single email in the mail queue.
|
|
type QueueEntry struct {
|
|
ID string `json:"id"` // Queue ID (hex string)
|
|
Sender string `json:"sender"` // From address
|
|
Recipients []string `json:"recipients"` // To addresses
|
|
Size int `json:"size"` // Size in bytes
|
|
Time string `json:"time"` // Arrival time
|
|
Reason string `json:"reason"` // Why it's deferred
|
|
}
|
|
|
|
// Regular expression for parsing human-readable queue output (not currently used).
|
|
var queueLineRegex = regexp.MustCompile(`^([A-Fa-f0-9]+)\s+(\d+)\s+(\w+)\s+([A-Za-z]+\s+\d+\s+[\d:]+)\s+([^\s]+)\s+(.+)$`)
|
|
|
|
// GetQueue retrieves all emails currently in the Postfix queue.
|
|
// Returns: a list of QueueEntry objects, or an error if the command fails.
|
|
// Uses `postqueue -j` to get JSON output from Postfix.
|
|
func GetQueue() ([]QueueEntry, error) {
|
|
// Execute postqueue command with JSON output flag.
|
|
// postqueue is the Postfix queue management utility.
|
|
cmd := exec.Command("/usr/sbin/postqueue", "-j")
|
|
var out bytes.Buffer
|
|
var stderr bytes.Buffer
|
|
cmd.Stdout = &out // Capture standard output
|
|
cmd.Stderr = &stderr // Capture standard error (for debugging)
|
|
|
|
err := cmd.Run()
|
|
if err != nil {
|
|
// Return detailed error including stderr for debugging.
|
|
return nil, fmt.Errorf("/usr/sbin/postqueue -j: %v (stderr: %s)", err, stderr.String())
|
|
}
|
|
|
|
// Parse the JSON output from postqueue.
|
|
// Each line of output is a separate JSON object.
|
|
var entries []QueueEntry
|
|
scanner := bufio.NewScanner(&out) // Read line by line
|
|
|
|
for scanner.Scan() {
|
|
line := strings.TrimSpace(scanner.Text())
|
|
// Skip empty lines and non-JSON lines.
|
|
if line == "" || !strings.HasPrefix(line, "{") {
|
|
continue
|
|
}
|
|
|
|
// Define the JSON structure matching postqueue's output format.
|
|
var entry struct {
|
|
QueueID string `json:"queueid"`
|
|
Sender string `json:"sender"`
|
|
Recipients []struct {
|
|
Address string `json:"address"`
|
|
} `json:"recipients"`
|
|
Size int64 `json:"size"`
|
|
Arrival string `json:"arrival_timestamp"`
|
|
Delay string `json:"delay_reason"`
|
|
}
|
|
|
|
// Parse the JSON line into our struct.
|
|
if err := json.Unmarshal([]byte(line), &entry); err != nil {
|
|
continue // Skip malformed JSON
|
|
}
|
|
|
|
// Extract recipient addresses from the nested structure.
|
|
recipients := make([]string, len(entry.Recipients))
|
|
for i, r := range entry.Recipients {
|
|
recipients[i] = r.Address
|
|
}
|
|
|
|
entries = append(entries, QueueEntry{
|
|
ID: entry.QueueID,
|
|
Sender: entry.Sender,
|
|
Recipients: recipients,
|
|
Size: int(entry.Size),
|
|
Time: entry.Arrival,
|
|
Reason: entry.Delay,
|
|
})
|
|
}
|
|
|
|
// Return empty slice instead of nil for consistent JSON output.
|
|
if entries == nil {
|
|
entries = []QueueEntry{}
|
|
}
|
|
|
|
return entries, nil
|
|
}
|
|
|
|
// RequeueMail requeues all deferred mail (attempts to resend them).
|
|
// This is a blanket operation - it requeues ALL deferred mail, not just one message.
|
|
// id: message ID (currently ignored, requeues all).
|
|
// Note: postqueue -f requeues everything, there's no per-message requeue.
|
|
func RequeueMail(id string) error {
|
|
// Execute postqueue -f to flush (requeue) all deferred mail.
|
|
cmd := exec.Command("/usr/sbin/postqueue", "-f", "-v")
|
|
var out bytes.Buffer
|
|
cmd.Stdout = &out
|
|
cmd.Stderr = &out
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// DeleteFromQueue deletes a specific message from the queue.
|
|
// id: the queue ID of the message to delete.
|
|
// Uses `postsuper -d <id>` to delete the message.
|
|
func DeleteFromQueue(id string) error {
|
|
// postsuper is the Postfix superuser queue management command.
|
|
// -d deletes messages by ID.
|
|
cmd := exec.Command("/usr/sbin/postsuper", "-d", id)
|
|
return cmd.Run()
|
|
}
|
|
|
|
// GetQueueCount returns the number of messages in the mail queue.
|
|
// Used for displaying queue statistics on the dashboard.
|
|
func GetQueueCount() (int, error) {
|
|
// Use JSON output to get queue count.
|
|
cmd := exec.Command("/usr/sbin/postqueue", "-j")
|
|
var out bytes.Buffer
|
|
cmd.Stdout = &out
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// Count the number of JSON objects (messages) in the output.
|
|
count := 0
|
|
scanner := bufio.NewScanner(&out)
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
// Each line of JSON output represents one message.
|
|
// Count non-empty lines that look like JSON.
|
|
if strings.TrimSpace(line) != "" && strings.HasPrefix(line, "{") {
|
|
count++
|
|
}
|
|
}
|
|
|
|
return count, nil
|
|
}
|