package main import ( "fmt" "log" "os" "path/filepath" "github.com/alexflint/go-arg" "github.com/emersion/go-imap" "github.com/emersion/go-imap/client" ) const Version = "0.0.1" func main() { var args struct { Address string `arg:"positional,required"` Username string `arg:"positional,required"` Password string `arg:"positional,required"` Output string `arg:"-o" help:"Output directory" default:"mail"` } if err := arg.Parse(&args); err != nil { switch err { case arg.ErrHelp: fmt.Print(help) os.Exit(0) case arg.ErrVersion: fmt.Print(version) os.Exit(0) } fmt.Println(err.Error()) os.Exit(-1) } c, err := client.DialTLS(args.Address, nil) if err != nil { log.Fatalln(err.Error()) } defer c.Logout() if err := c.Login(args.Username, args.Password); err != nil { log.Fatalln(err.Error()) } mailboxes, err := Mailboxes(c) if err != nil { log.Fatalln(err.Error()) } for _, m := range mailboxes { if m.Name == "Trash" { log.Println("Skipping Trash") continue } log.Println(m.Name) if err := FetchMailbox(c, m, args.Output); err != nil { log.Fatalln(err.Error()) } } } /* Get list of Mailboxes. */ func Mailboxes(c *client.Client) ([]*imap.MailboxInfo, error) { ch := make(chan *imap.MailboxInfo, 6) done := make(chan error, 1) go func() { done <- c.List("", "*", ch) }() var mailboxes []*imap.MailboxInfo for m := range ch { mailboxes = append(mailboxes, m) } if err := <-done; err != nil { return nil, err } return mailboxes, nil } func FetchMailbox(c *client.Client, mailbox *imap.MailboxInfo, dir string) error { mbox, err := c.Select(mailbox.Name, true) if err != nil { return err } if err := os.MkdirAll(filepath.Join(dir, mailbox.Name), 0o777); err != nil { return err } if mbox.Messages == 0 { return nil } seqset := new(imap.SeqSet) seqset.AddRange(1, mbox.Messages) section := &imap.BodySectionName{Peek: false} items := []imap.FetchItem{imap.FetchAll, section.FetchItem()} messages := make(chan *imap.Message, 10) done := make(chan error, 1) go func() { done <- c.Fetch(seqset, items, messages) }() for msg := range messages { body := msg.GetBody(section) if body == nil { return fmt.Errorf("message body is nil") } data := make([]byte, body.Len()) if _, err := body.Read(data); err != nil { return err } file, err := os.Create( filepath.Join(dir, mailbox.Name, fmt.Sprintf( "%s_%d.eml", msg.InternalDate.UTC().Format("20060102T150405Z"), msg.SeqNum, )), ) if err != nil { return err } if _, err := file.Write(data); err != nil { file.Close() return err } log.Println(mailbox.Name, "->", msg.Envelope.Subject) file.Close() } if err := <-done; err != nil { return err } return nil } const help = `` const version = "backup-imap, version " + Version + "\n"