119 lines
3.6 KiB
Go
119 lines
3.6 KiB
Go
package parser
|
|
|
|
import (
|
|
"bufio"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type QEntry struct {
|
|
Id string
|
|
Status string
|
|
Date time.Time
|
|
Size int
|
|
Sender string
|
|
Recipients []string
|
|
Reason string
|
|
}
|
|
|
|
type MailQ struct {
|
|
NumTotal int
|
|
NumActive int
|
|
NumHold int
|
|
NumDeferred int
|
|
Entries []QEntry
|
|
}
|
|
|
|
const SortableDateFormat = "2006-01-02 15:04:05"
|
|
|
|
func (m QEntry) ShortString() string {
|
|
return fmt.Sprintf("[%s] %s <%s> -> %d recipients (%d bytes)", m.Date.Format(SortableDateFormat), m.Id, m.Sender, len(m.Recipients), m.Size)
|
|
}
|
|
|
|
func (m QEntry) String() string {
|
|
var recipientSuffix string
|
|
if len(m.Recipients) > 1 {
|
|
recipientSuffix = ",..."
|
|
}
|
|
return fmt.Sprintf("[%s] %s <%s> -> {%d}<%s>%s (%s, %d bytes)", m.Date.Format(SortableDateFormat), m.Id, m.Sender, len(m.Recipients), m.Recipients[0], recipientSuffix, m.Status, m.Size)
|
|
}
|
|
|
|
func (m QEntry) DetailedString() string {
|
|
var reasonStr string
|
|
if m.Reason == "" {
|
|
reasonStr = "-/-"
|
|
}
|
|
return fmt.Sprintf("Id: %s\nDate: %s\nStatus: %s\nReason: %s\nSize: %d\nSender: %s\nRecipients: %s", m.Id, m.Date.Format(SortableDateFormat), m.Status, reasonStr, m.Size, m.Sender, strings.Join(m.Recipients, ", "))
|
|
}
|
|
|
|
func ParseMailQ(dataSource io.Reader) (MailQ, error) {
|
|
const dateFormat = "2006 Mon Jan _2 15:04:05"
|
|
var messageIdStart = regexp.MustCompile("^[0-9A-Za-z]+[*!]? ")
|
|
scanner := bufio.NewScanner(dataSource)
|
|
var line string
|
|
scanner.Scan()
|
|
line = scanner.Text()
|
|
if strings.HasPrefix(line, "Mail queue is empty") {
|
|
// If mail queue is empty, there is nothing to do
|
|
return MailQ{}, nil
|
|
} else if strings.HasPrefix(line, "-Queue ID-") == false {
|
|
// Abort if input does not look like output from mailq(1)
|
|
return MailQ{}, errors.New("Sorry, this does not look like output from mailq(1).")
|
|
}
|
|
var queue MailQ
|
|
var currentMail QEntry
|
|
for scanner.Scan() {
|
|
// Read input line by line
|
|
line := scanner.Text()
|
|
fields := strings.Fields(line)
|
|
if strings.HasPrefix(line, "--") {
|
|
// Handle the summary line at the end of mailq output
|
|
break
|
|
} else if messageIdStart.MatchString(line) {
|
|
// Handle line starting next mail in queue
|
|
messageId := fields[0]
|
|
queue.NumTotal += 1
|
|
if strings.HasSuffix(messageId, "*") {
|
|
queue.NumActive += 1
|
|
currentMail.Status = "active"
|
|
} else if strings.HasSuffix(messageId, "!") {
|
|
currentMail.Status = "hold"
|
|
queue.NumHold += 1
|
|
} else {
|
|
currentMail.Status = "deferred"
|
|
queue.NumDeferred += 1
|
|
}
|
|
dateString := strconv.Itoa(time.Now().Year()) + " " + strings.Join(fields[2:6], " ")
|
|
mailDate, _ := time.Parse(dateFormat, dateString)
|
|
currentMail.Id = strings.TrimRight(messageId, "*!")
|
|
currentMail.Size, _ = strconv.Atoi(fields[1])
|
|
currentMail.Date = mailDate
|
|
currentMail.Sender = fields[6]
|
|
continue
|
|
} else if strings.HasPrefix(line, "(") && strings.HasSuffix(line, ")") {
|
|
// Handle reason for deferred status (if deferred at all, may be missing)
|
|
currentMail.Reason = strings.Trim(strings.TrimSpace(line), "()")
|
|
continue
|
|
} else if len(fields) == 1 {
|
|
// Handle line with one of the mail recipients
|
|
currentMail.Recipients = append(currentMail.Recipients, fields[0])
|
|
continue
|
|
} else if len(fields) == 0 {
|
|
// If the next line is empty, make sure to push current mail to list
|
|
// and create a new struct for the next mail to process
|
|
queue.Entries = append(queue.Entries, currentMail)
|
|
currentMail = QEntry{}
|
|
}
|
|
}
|
|
if scanner.Err() != nil {
|
|
// If the scanner failed, let our caller know.
|
|
return MailQ{}, errors.New("Something went wrong with reading from stdin. Sorry!")
|
|
}
|
|
return queue, nil
|
|
}
|