package main import ( "bytes" "fmt" "html/template" "io" "log" "mime" "strconv" "strings" /* _ "golang.org/x/net/html" _ "golang.org/x/net/html/atom" */ "github.com/emersion/go-imap/v2" "github.com/emersion/go-imap/v2/imapclient" "github.com/emersion/go-message/charset" "github.com/emersion/go-message/mail" "golang.org/x/net/html" ) type MailBox struct { Server string Port uint16 User string Password string SSL string InBox string Client *imapclient.Client } func (mb *MailBox) Configure(conf *MailBoxConfiguration) error { mb.Server = conf.Server port, err := strconv.ParseUint(conf.Port, 10, 16) if err != nil { return err } mb.Port = uint16(port) mb.User = conf.User mb.Password = conf.Password mb.SSL = conf.SSL mb.InBox = conf.InBox return nil } func (mb *MailBox) ListMessages() (string, error) { return "", nil } func (mb *MailBox) Connect() error { options := &imapclient.Options{ WordDecoder: &mime.WordDecoder{CharsetReader: charset.Reader}, } var err error imapServer := fmt.Sprintf("%s:%d", mb.Server, mb.Port) switch mb.SSL { case "SSL/TLS": mb.Client, err = imapclient.DialTLS(imapServer, options) case "StartTLS": mb.Client, err = imapclient.DialStartTLS(imapServer, options) case "NoTLS": mb.Client, err = imapclient.DialInsecure(imapServer, options) default: return fmt.Errorf("bad tls configuration") } if err != nil { log.Println("Error connnecting to", imapServer, ":", err) return err } log.Println("Connected") err = mb.Client.Login(mb.User, mb.Password).Wait() if err != nil { log.Fatal("failed to login:", err) } log.Println("Logged in") return nil } func (mb *MailBox) ListFolders() ([]string, error) { folders := make([]string, 0) mailBoxes, err := mb.Client.List("", "%", nil).Collect() if err != nil { return nil, err } for _, mbox := range mailBoxes { folders = append(folders, mbox.Mailbox) } return folders, nil } func (mb *MailBox) GetMessages() ([]Post, error) { inbox, err := mb.Client.Select(mb.InBox, nil).Wait() if err != nil { log.Fatal("Error selecting mailbox:", mb.InBox, err) } log.Println("Inbox has", inbox.NumMessages, "messages") if inbox.NumMessages <= 0 { return nil, fmt.Errorf("nomessages") } posts := make([]Post, 0) for i := uint32(1); i <= inbox.NumMessages; i++ { var post Post seqSet := imap.SeqSetNum(i) bodySection := &imap.FetchItemBodySection{} fetchOptions := &imap.FetchOptions{ Envelope: true, BodySection: []*imap.FetchItemBodySection{bodySection}, } messages, err := mb.Client.Fetch(seqSet, fetchOptions).Collect() if err != nil { log.Fatal("Error fetching mails:", err) } post.Title = strings.Trim(messages[0].Envelope.Subject, "\t ") if !strings.HasPrefix(strings.ToLower(post.Title), "blog ") { continue } post.Id = messages[0].Envelope.MessageID post.Title = post.Title[5:] // remove first chars = blog+whitespace post.Author = messages[0].Envelope.To[0].Name post.Date = messages[0].Envelope.Date.Format("2006/01/02 15:04") section := messages[0].FindBodySection(bodySection) ioReader := bytes.NewReader(section) mailReader, err := mail.CreateReader(ioReader) if err != nil { return nil, err } for { part, err := mailReader.NextPart() if err == io.EOF { break } else if err != nil { return nil, err } switch part.Header.(type) { case *mail.AttachmentHeader: /* filename, _ := header.Filename() log.Println("------------FILE-----------") log.Println("Attachment:", filename) */ case *mail.InlineHeader: body, _ := io.ReadAll(part.Body) contentTypeFull := strings.TrimPrefix( strings.ToLower(part.Header.Get("Content-Type")), "content-type") mediaType, _, err := mime.ParseMediaType(strings.TrimLeft(contentTypeFull, ": ")) if err != nil { return nil, err } switch mediaType { case "text/plain": if len(post.HTML) == 0 { post.Text = string(body) post.GenerateExcerpt() } case "text/html": post.HTML = "" nodes, err := html.Parse(strings.NewReader(string(body))) if err != nil { post.HTML = "" break } htmlNode, err := findHTMLNode(nodes) if err != nil { post.HTML = "" break } var buf bytes.Buffer err = nil for child := htmlNode.FirstChild; child != nil; child = child.NextSibling { err = html.Render(&buf, child) if err != nil { post.HTML = "" break } } if err == nil { post.HTML = template.HTML(buf.String()) post.GenerateExcerpt() } default: log.Println("Content-Type:", part.Header.Get("Content-Type")) } default: log.Printf("Unkown part type %+v", part) } } posts = append(posts, post) } return posts, nil } func (mb *MailBox) Close() { mb.Client.Close() } func findHTMLNode(node *html.Node) (*html.Node, error) { if node.Type == html.ElementNode && node.Data == "body" { return node, nil } for e := node.FirstChild; e != nil; e = node.NextSibling { n, err := findHTMLNode(e) if err == nil { return n, nil } } return nil, fmt.Errorf("notfound") }