Search code examples
gogmailimap

Golang Gmail imap search is freezing


I am trying to do a search for specific sender or specific flag. When I select Inbox and do a search on specific string the search api freezes. I am not sure what am I doing wrong. I am not able to find much examples too.

criteria := imap.NewSearchCriteria()
criteria.WithoutFlags = []string{"\\Seen"}
uids, err := c.Search(criteria)
if err != nil {
 log.Println(err)
}
seqset := new(imap.SeqSet)
seqset.AddNum(uids...)
section := &imap.BodySectionName{}
items := []imap.FetchItem{imap.FetchEnvelope, imap.FetchFlags, imap.FetchInternalDate, section.FetchItem()}
messages := make(chan *imap.Message)
go func() {
    done <- c.Fetch(seqset, items, messages)
}()
msg := <-messages //its getting stuck at this point. Nothing happens i dont get messages.

Solution

  • When your search can return more than one message, you need to make sure to read everything from the messages channel, otherwise the program will hang. So instead of:

    msg := <-messages
    

    loop over the channel like so:

    for msg := range messages {
      // process msg
    }
    

    Here's my working sample, which pulls connection details from the environment. It's a modified version of the example from emersion's imap lib:

    package main
    
    import (
        "crypto/tls"
        "flag"
        "fmt"
        "log"
        "os"
        "time"
    
        "github.com/emersion/go-imap"
        "github.com/emersion/go-imap/client"
    )
    
    func main() {
        hours := flag.Int("hours", 24, "look this many hours in the past for messages")
        flag.Parse()
        if *hours > 0 {
            *hours = *hours * -1
        }
    
        host := os.Getenv("IMAP_HOST")
        port := os.Getenv("IMAP_PORT")
        user := os.Getenv("IMAP_USER")
        pass := os.Getenv("IMAP_PASS")
        tlsn := os.Getenv("IMAP_TLS_SERVERNAME")
        if port == "" {
            port = "993"
        }
    
        connStr := fmt.Sprintf("%s:%s", host, port)
    
        tlsc := &tls.Config{}
        if tlsn != "" {
            tlsc.ServerName = tlsn
        }
    
        c, err := client.DialTLS(connStr, tlsc)
        if err != nil {
            log.Fatal(err)
        }
        log.Println("Connected")
        defer c.Logout()
    
        if err := c.Login(user, pass); err != nil {
            log.Fatal(err)
        }
        log.Println("Authenticated")
    
        mbox, err := c.Select("INBOX", false)
        if err != nil {
            log.Fatal(err)
        }
        log.Println("Flags for INBOX:", mbox.Flags)
    
        criteria := imap.NewSearchCriteria()
        //criteria.WithFlags = []string{imap.SeenFlag}
        criteria.Since = time.Now().Add(time.Duration(*hours) * time.Hour)
        uids, err := c.Search(criteria)
        if err != nil {
            log.Println(err)
        }
        seqset := new(imap.SeqSet)
        seqset.AddNum(uids...)
        log.Printf("Search complete, found %d messages", len(uids))
    
        section := &imap.BodySectionName{}
        items := []imap.FetchItem{imap.FetchEnvelope, imap.FetchFlags, imap.FetchInternalDate, section.FetchItem()}
        messages := make(chan *imap.Message)
        done := make(chan error, 1)
        go func() {
            done <- c.Fetch(seqset, items, messages)
            log.Println("Fetch complete")
        }()
        for msg := range messages {
            if msg != nil {
                log.Printf("got message with address %p\n", msg)
            } else {
                log.Println("no messages matched criteria")
            }
        }
        if err := <-done; err != nil {
            log.Fatal(err)
        }
    }