// 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 ` 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 }