|
@@ -1,15 +1,23 @@
|
|
package main
|
|
package main
|
|
|
|
|
|
import (
|
|
import (
|
|
|
|
+ "errors"
|
|
"flag"
|
|
"flag"
|
|
"fmt"
|
|
"fmt"
|
|
"io"
|
|
"io"
|
|
|
|
+ "io/ioutil"
|
|
|
|
+ "math"
|
|
"os"
|
|
"os"
|
|
|
|
+ "strconv"
|
|
|
|
+ "strings"
|
|
|
|
|
|
"git.lattuga.net/blallo/papero/cli"
|
|
"git.lattuga.net/blallo/papero/cli"
|
|
"git.lattuga.net/blallo/papero/imaputils"
|
|
"git.lattuga.net/blallo/papero/imaputils"
|
|
|
|
+ "github.com/emersion/go-imap"
|
|
)
|
|
)
|
|
|
|
|
|
|
|
+const maxUInt32 = int(^uint32(0))
|
|
|
|
+
|
|
var infoVerbs cli.CommandMap
|
|
var infoVerbs cli.CommandMap
|
|
|
|
|
|
func init() {
|
|
func init() {
|
|
@@ -54,11 +62,19 @@ func (i InfoCmd) Help(w io.Writer, set *flag.FlagSet) {
|
|
|
|
|
|
type LsMailboxCmd struct{}
|
|
type LsMailboxCmd struct{}
|
|
|
|
|
|
|
|
+type mailboxInfo struct {
|
|
|
|
+ name string
|
|
|
|
+ attrs string
|
|
|
|
+}
|
|
|
|
+
|
|
func (l LsMailboxCmd) Func(args []string) error {
|
|
func (l LsMailboxCmd) Func(args []string) error {
|
|
if Session.Info.Opts.Debug {
|
|
if Session.Info.Opts.Debug {
|
|
Log.Debug("enter info list-mailboxes")
|
|
Log.Debug("enter info list-mailboxes")
|
|
}
|
|
}
|
|
var withAttributes bool
|
|
var withAttributes bool
|
|
|
|
+ var output string
|
|
|
|
+ var maxLen int
|
|
|
|
+ var mailboxes []mailboxInfo
|
|
flagset := flag.NewFlagSet(args[0], flag.ExitOnError)
|
|
flagset := flag.NewFlagSet(args[0], flag.ExitOnError)
|
|
flagset.BoolVar(&withAttributes, "with-attrs", false, "toggle attributes display")
|
|
flagset.BoolVar(&withAttributes, "with-attrs", false, "toggle attributes display")
|
|
flagset.Usage = func() { l.Help(os.Stdout, flagset) }
|
|
flagset.Usage = func() { l.Help(os.Stdout, flagset) }
|
|
@@ -73,17 +89,30 @@ func (l LsMailboxCmd) Func(args []string) error {
|
|
}
|
|
}
|
|
for _, box := range mboxInfo {
|
|
for _, box := range mboxInfo {
|
|
if withAttributes {
|
|
if withAttributes {
|
|
- attrs := fmt.Sprint(box.Attributes)
|
|
|
|
- fmt.Printf("%s (attrs: %s)", box.Name, attrs)
|
|
|
|
|
|
+ if nameLen := len(box.Name); nameLen > maxLen {
|
|
|
|
+ maxLen = nameLen
|
|
|
|
+ }
|
|
|
|
+ mailboxes = append(mailboxes, mailboxInfo{name: box.Name, attrs: fmt.Sprint(box.Attributes)})
|
|
} else {
|
|
} else {
|
|
- fmt.Println(box.Name)
|
|
|
|
|
|
+ output += fmt.Sprintln(box.Name)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if maxLen == 0 {
|
|
|
|
+ fmt.Print(output)
|
|
|
|
+ } else {
|
|
|
|
+ padFmtStr := fmt.Sprintf("%%-%dv\t", maxLen)
|
|
|
|
+ for _, m := range mailboxes {
|
|
|
|
+ line := fmt.Sprintf(padFmtStr, m.name)
|
|
|
|
+ line += m.attrs
|
|
|
|
+ fmt.Println(line)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
func (l LsMailboxCmd) Help(w io.Writer, set *flag.FlagSet) {
|
|
func (l LsMailboxCmd) Help(w io.Writer, set *flag.FlagSet) {
|
|
- fmt.Fprintf(w, "USAGE: %s info list-mailboxes\n", Session.Info.Name)
|
|
|
|
|
|
+ fmt.Fprintf(w, "USAGE: %s info list-mailboxes [opts]\n", Session.Info.Name)
|
|
|
|
+ fmt.Fprintf(w, "\nOPTS:\n")
|
|
set.PrintDefaults()
|
|
set.PrintDefaults()
|
|
}
|
|
}
|
|
|
|
|
|
@@ -94,8 +123,14 @@ func (l LsMsgCmd) Func(args []string) error {
|
|
Log.Debug("enter info list-messages")
|
|
Log.Debug("enter info list-messages")
|
|
}
|
|
}
|
|
var limit uint
|
|
var limit uint
|
|
|
|
+ var withSeq, withFrom, withDate bool
|
|
|
|
+ var sep string
|
|
flagset := flag.NewFlagSet(args[0], flag.ExitOnError)
|
|
flagset := flag.NewFlagSet(args[0], flag.ExitOnError)
|
|
|
|
+ flagset.StringVar(&sep, "sep", "\t", "separator between fields")
|
|
flagset.UintVar(&limit, "limit", 0, "maximum number of messages to display (0 means no limit)")
|
|
flagset.UintVar(&limit, "limit", 0, "maximum number of messages to display (0 means no limit)")
|
|
|
|
+ flagset.BoolVar(&withSeq, "seq", false, "show sequence number")
|
|
|
|
+ flagset.BoolVar(&withFrom, "from", false, "show From address")
|
|
|
|
+ flagset.BoolVar(&withDate, "date", false, "show message date")
|
|
flagset.Usage = func() { l.Help(os.Stdout, flagset) }
|
|
flagset.Usage = func() { l.Help(os.Stdout, flagset) }
|
|
flagset.Parse(args[1:])
|
|
flagset.Parse(args[1:])
|
|
|
|
|
|
@@ -104,6 +139,12 @@ func (l LsMsgCmd) Func(args []string) error {
|
|
l.Help(os.Stderr, flagset)
|
|
l.Help(os.Stderr, flagset)
|
|
os.Exit(1)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ msgs, err := imaputils.ListMessages(Session.Config, uint32(limit), Session.Info.Opts.Debug, subArgs[0])
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ printMsgs(msgs, withSeq, withFrom, withDate, sep)
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
@@ -113,25 +154,154 @@ func (l LsMsgCmd) Help(w io.Writer, set *flag.FlagSet) {
|
|
set.PrintDefaults()
|
|
set.PrintDefaults()
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+func numberSize(n uint32) int {
|
|
|
|
+ return int(math.Floor(math.Log10(float64(n))))
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func printMsgs(msgs []*imap.Message, withSeq, withFrom, withDate bool, sep string) {
|
|
|
|
+ var maxSeq, maxDateLen, maxFromLen int
|
|
|
|
+ var out, seqFmtStr, dateFmtStr, fromFmtStr string
|
|
|
|
+ for _, msg := range msgs {
|
|
|
|
+ if seqSize := numberSize(msg.SeqNum); withSeq && seqSize > maxSeq {
|
|
|
|
+ maxSeq = seqSize
|
|
|
|
+ }
|
|
|
|
+ if withDate {
|
|
|
|
+ date := fmt.Sprint(msg.Envelope.Date)
|
|
|
|
+ if dateLen := len(date); dateLen > maxDateLen {
|
|
|
|
+ maxDateLen = dateLen
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if withFrom {
|
|
|
|
+ for _, from := range msg.Envelope.From {
|
|
|
|
+ if fromLen := len(from.Address()); fromLen > maxFromLen {
|
|
|
|
+ maxFromLen = fromLen
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if withSeq {
|
|
|
|
+ seqFmtStr = fmt.Sprintf("%%%dv%s", maxSeq, sep)
|
|
|
|
+ }
|
|
|
|
+ if withDate {
|
|
|
|
+ dateFmtStr = fmt.Sprintf("%%-%dv%s", maxDateLen, sep)
|
|
|
|
+ }
|
|
|
|
+ if withFrom {
|
|
|
|
+ fromFmtStr = fmt.Sprintf("%%-%dv%s", maxFromLen, sep)
|
|
|
|
+ }
|
|
|
|
+ for _, msg := range msgs {
|
|
|
|
+ var line string
|
|
|
|
+ if withSeq {
|
|
|
|
+ line += fmt.Sprintf(seqFmtStr, msg.SeqNum)
|
|
|
|
+ }
|
|
|
|
+ if withDate {
|
|
|
|
+ line += fmt.Sprintf(dateFmtStr, msg.Envelope.Date)
|
|
|
|
+ }
|
|
|
|
+ if withFrom {
|
|
|
|
+ line += fmt.Sprintf(fromFmtStr, msg.Envelope.From[0].Address())
|
|
|
|
+ }
|
|
|
|
+ line += msg.Envelope.Subject
|
|
|
|
+ out += fmt.Sprintln(line)
|
|
|
|
+ }
|
|
|
|
+ fmt.Println(out)
|
|
|
|
+}
|
|
|
|
+
|
|
type CatMsgCmd struct{}
|
|
type CatMsgCmd struct{}
|
|
|
|
|
|
func (c CatMsgCmd) Func(args []string) error {
|
|
func (c CatMsgCmd) Func(args []string) error {
|
|
if Session.Info.Opts.Debug {
|
|
if Session.Info.Opts.Debug {
|
|
Log.Debug("enter info display-message")
|
|
Log.Debug("enter info display-message")
|
|
}
|
|
}
|
|
|
|
+ var idList []uint32
|
|
flagset := flag.NewFlagSet(args[0], flag.ExitOnError)
|
|
flagset := flag.NewFlagSet(args[0], flag.ExitOnError)
|
|
flagset.Usage = func() { c.Help(os.Stdout, flagset) }
|
|
flagset.Usage = func() { c.Help(os.Stdout, flagset) }
|
|
flagset.Parse(args[1:])
|
|
flagset.Parse(args[1:])
|
|
|
|
|
|
subArgs := flagset.Args()
|
|
subArgs := flagset.Args()
|
|
- if len(subArgs) != 1 {
|
|
|
|
|
|
+ lenSubArgs := len(subArgs)
|
|
|
|
+ if lenSubArgs < 2 {
|
|
c.Help(os.Stderr, flagset)
|
|
c.Help(os.Stderr, flagset)
|
|
os.Exit(1)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ mailbox := subArgs[0]
|
|
|
|
+
|
|
|
|
+ if lenSubArgs > 1 {
|
|
|
|
+ for _, id := range subArgs[1:] {
|
|
|
|
+ mailId, err := parseToUint32(id)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ idList = append(idList, mailId)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ messages, err := imaputils.FetchMessages(Session.Config, mailbox, idList, Session.Info.Opts.Debug)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for _, m := range messages {
|
|
|
|
+ err = printMessage(m)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ }
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
func (c CatMsgCmd) Help(w io.Writer, set *flag.FlagSet) {
|
|
func (c CatMsgCmd) Help(w io.Writer, set *flag.FlagSet) {
|
|
- fmt.Fprintf(w, "USAGE: %s info display-message MAILBOX_NAME MESSAGE_ID\n", Session.Info.Name)
|
|
|
|
|
|
+ fmt.Fprintf(w, "USAGE: %s info display-message [opts] MAILBOX_NAME [MESSAGE_ID1 [MESSAGE_ID2 [...]]]\n", Session.Info.Name)
|
|
|
|
+ fmt.Fprintf(w, "\nOPTS:\n")
|
|
set.PrintDefaults()
|
|
set.PrintDefaults()
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+var ErrOutOfBounds = errors.New("number is out of bounds")
|
|
|
|
+
|
|
|
|
+func parseToUint32(s string) (uint32, error) {
|
|
|
|
+ out, err := strconv.Atoi(s)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return 0, err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if out < 0 || out > maxUInt32 {
|
|
|
|
+ return 0, ErrOutOfBounds
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return uint32(out), nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func printMessage(m *imap.Message) error {
|
|
|
|
+ var out string
|
|
|
|
+
|
|
|
|
+ out += fmt.Sprintln(formatAddresses(m.Envelope.From, "From"))
|
|
|
|
+ out += fmt.Sprintln(formatAddresses(m.Envelope.Sender, "Sender"))
|
|
|
|
+ out += fmt.Sprintln(formatAddresses(m.Envelope.Cc, "Cc"))
|
|
|
|
+ out += fmt.Sprintln(formatAddresses(m.Envelope.Bcc, "Bcc"))
|
|
|
|
+ out += fmt.Sprintln(formatAddresses(m.Envelope.ReplyTo, "ReplyTo"))
|
|
|
|
+ out += fmt.Sprintf("InReplyTo: %s\n", m.Envelope.InReplyTo)
|
|
|
|
+ out += fmt.Sprintf("Date: %v\n", m.Envelope.Date)
|
|
|
|
+ out += fmt.Sprintf("Subject: %s\n", m.Envelope.Subject)
|
|
|
|
+
|
|
|
|
+ bodyReader := m.GetBody(&imap.BodySectionName{BodyPartName: imap.BodyPartName{}, Peek: true})
|
|
|
|
+ body, err := ioutil.ReadAll(bodyReader)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ out += fmt.Sprintln(string(body))
|
|
|
|
+
|
|
|
|
+ fmt.Println(out)
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func formatAddresses(addresses []*imap.Address, name string) string {
|
|
|
|
+ out := fmt.Sprintf("%s: ", name)
|
|
|
|
+ for _, address := range addresses {
|
|
|
|
+ if address.PersonalName != "" {
|
|
|
|
+ out += fmt.Sprintf("%s <%s>, ", address.PersonalName, address.Address())
|
|
|
|
+ } else {
|
|
|
|
+ out += fmt.Sprintf("%s, ", address.Address())
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return strings.TrimRight(out, ", ")
|
|
|
|
+}
|