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 } func (m QEntry) String() string { return fmt.Sprintf("ID: %s, Status: %s, Size: %d bytes, Sender: %s, Recipients: %d", m.id, m.status, m.size, m.sender, len(m.recipients)) } func ParseMailQ(dataSource io.Reader) ([]QEntry, error) { const dateFormat = "2006 Mon Jan _2 15:04:05" var messageIdStart = regexp.MustCompile("^[0-9A-Za-z]+[*!]? ") scanner := bufio.NewScanner(dataSource) scanner.Scan() line := scanner.Text() if strings.HasPrefix(line, "Mail queue is empty") { // If mail queue is empty, there is nothing to do return nil, errors.New(line) } else if strings.HasPrefix(line, "-Queue ID-") == false { // Abort if input does not look like output from mailq(1) return nil, errors.New("Sorry, this does not look like output from mailq(1).") } var currentMail QEntry var queueEntries []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] if strings.HasSuffix(messageId, "*") { currentMail.status = "active" } else if strings.HasSuffix(messageId, "!") { currentMail.status = "hold" } else { currentMail.status = "deferred" } 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 queueEntries = append(queueEntries, currentMail) currentMail = QEntry{} } } if scanner.Err() != nil { // If the scanner failed, let our caller know. return nil, errors.New("Something went wrong with reading from stdin. Sorry!") } return queueEntries, nil }