package imapclient_test import ( "io" "log" "time" "github.com/emersion/go-message/mail" "github.com/emersion/go-sasl" "github.com/emersion/go-imap/v2" "github.com/emersion/go-imap/v2/imapclient" ) func ExampleClient() { c, err := imapclient.DialTLS("mail.example.org:993", nil) if err != nil { log.Fatalf("failed to dial IMAP server: %v", err) } defer c.Close() if err := c.Login("root", "asdf").Wait(); err != nil { log.Fatalf("failed to login: %v", err) } mailboxes, err := c.List("", "%", nil).Collect() if err != nil { log.Fatalf("failed to list mailboxes: %v", err) } log.Printf("Found %v mailboxes", len(mailboxes)) for _, mbox := range mailboxes { log.Printf(" - %v", mbox.Mailbox) } selectedMbox, err := c.Select("INBOX", nil).Wait() if err != nil { log.Fatalf("failed to select INBOX: %v", err) } log.Printf("INBOX contains %v messages", selectedMbox.NumMessages) if selectedMbox.NumMessages > 0 { seqSet := imap.SeqSetNum(1) fetchOptions := &imap.FetchOptions{Envelope: true} messages, err := c.Fetch(seqSet, fetchOptions).Collect() if err != nil { log.Fatalf("failed to fetch first message in INBOX: %v", err) } log.Printf("subject of first message in INBOX: %v", messages[0].Envelope.Subject) } if err := c.Logout().Wait(); err != nil { log.Fatalf("failed to logout: %v", err) } } func ExampleClient_pipelining() { var c *imapclient.Client uid := imap.UID(42) fetchOptions := &imap.FetchOptions{Envelope: true} // Login, select and fetch a message in a single roundtrip loginCmd := c.Login("root", "root") selectCmd := c.Select("INBOX", nil) fetchCmd := c.Fetch(imap.UIDSetNum(uid), fetchOptions) if err := loginCmd.Wait(); err != nil { log.Fatalf("failed to login: %v", err) } if _, err := selectCmd.Wait(); err != nil { log.Fatalf("failed to select INBOX: %v", err) } if messages, err := fetchCmd.Collect(); err != nil { log.Fatalf("failed to fetch message: %v", err) } else { log.Printf("Subject: %v", messages[0].Envelope.Subject) } } func ExampleClient_Append() { var c *imapclient.Client buf := []byte("From: \r\n\r\nHi <3") size := int64(len(buf)) appendCmd := c.Append("INBOX", size, nil) if _, err := appendCmd.Write(buf); err != nil { log.Fatalf("failed to write message: %v", err) } if err := appendCmd.Close(); err != nil { log.Fatalf("failed to close message: %v", err) } if _, err := appendCmd.Wait(); err != nil { log.Fatalf("APPEND command failed: %v", err) } } func ExampleClient_Status() { var c *imapclient.Client options := imap.StatusOptions{NumMessages: true} if data, err := c.Status("INBOX", &options).Wait(); err != nil { log.Fatalf("STATUS command failed: %v", err) } else { log.Printf("INBOX contains %v messages", *data.NumMessages) } } func ExampleClient_List_stream() { var c *imapclient.Client // ReturnStatus requires server support for IMAP4rev2 or LIST-STATUS listCmd := c.List("", "%", &imap.ListOptions{ ReturnStatus: &imap.StatusOptions{ NumMessages: true, NumUnseen: true, }, }) for { mbox := listCmd.Next() if mbox == nil { break } log.Printf("Mailbox %q contains %v messages (%v unseen)", mbox.Mailbox, mbox.Status.NumMessages, mbox.Status.NumUnseen) } if err := listCmd.Close(); err != nil { log.Fatalf("LIST command failed: %v", err) } } func ExampleClient_Store() { var c *imapclient.Client seqSet := imap.SeqSetNum(1) storeFlags := imap.StoreFlags{ Op: imap.StoreFlagsAdd, Flags: []imap.Flag{imap.FlagFlagged}, Silent: true, } if err := c.Store(seqSet, &storeFlags, nil).Close(); err != nil { log.Fatalf("STORE command failed: %v", err) } } func ExampleClient_Fetch() { var c *imapclient.Client seqSet := imap.SeqSetNum(1) bodySection := &imap.FetchItemBodySection{Specifier: imap.PartSpecifierHeader} fetchOptions := &imap.FetchOptions{ Flags: true, Envelope: true, BodySection: []*imap.FetchItemBodySection{bodySection}, } messages, err := c.Fetch(seqSet, fetchOptions).Collect() if err != nil { log.Fatalf("FETCH command failed: %v", err) } msg := messages[0] header := msg.FindBodySection(bodySection) log.Printf("Flags: %v", msg.Flags) log.Printf("Subject: %v", msg.Envelope.Subject) log.Printf("Header:\n%v", string(header)) } func ExampleClient_Fetch_streamBody() { var c *imapclient.Client seqSet := imap.SeqSetNum(1) bodySection := &imap.FetchItemBodySection{} fetchOptions := &imap.FetchOptions{ UID: true, BodySection: []*imap.FetchItemBodySection{bodySection}, } fetchCmd := c.Fetch(seqSet, fetchOptions) defer fetchCmd.Close() for { msg := fetchCmd.Next() if msg == nil { break } for { item := msg.Next() if item == nil { break } switch item := item.(type) { case imapclient.FetchItemDataUID: log.Printf("UID: %v", item.UID) case imapclient.FetchItemDataBodySection: b, err := io.ReadAll(item.Literal) if err != nil { log.Fatalf("failed to read body section: %v", err) } log.Printf("Body:\n%v", string(b)) } } } if err := fetchCmd.Close(); err != nil { log.Fatalf("FETCH command failed: %v", err) } } func ExampleClient_Fetch_parseBody() { var c *imapclient.Client // Send a FETCH command to fetch the message body seqSet := imap.SeqSetNum(1) bodySection := &imap.FetchItemBodySection{} fetchOptions := &imap.FetchOptions{ BodySection: []*imap.FetchItemBodySection{bodySection}, } fetchCmd := c.Fetch(seqSet, fetchOptions) defer fetchCmd.Close() msg := fetchCmd.Next() if msg == nil { log.Fatalf("FETCH command did not return any message") } // Find the body section in the response var bodySectionData imapclient.FetchItemDataBodySection ok := false for { item := msg.Next() if item == nil { break } bodySectionData, ok = item.(imapclient.FetchItemDataBodySection) if ok { break } } if !ok { log.Fatalf("FETCH command did not return body section") } // Read the message via the go-message library mr, err := mail.CreateReader(bodySectionData.Literal) if err != nil { log.Fatalf("failed to create mail reader: %v", err) } // Print a few header fields h := mr.Header if date, err := h.Date(); err != nil { log.Printf("failed to parse Date header field: %v", err) } else { log.Printf("Date: %v", date) } if to, err := h.AddressList("To"); err != nil { log.Printf("failed to parse To header field: %v", err) } else { log.Printf("To: %v", to) } if subject, err := h.Text("Subject"); err != nil { log.Printf("failed to parse Subject header field: %v", err) } else { log.Printf("Subject: %v", subject) } // Process the message's parts for { p, err := mr.NextPart() if err == io.EOF { break } else if err != nil { log.Fatalf("failed to read message part: %v", err) } switch h := p.Header.(type) { case *mail.InlineHeader: // This is the message's text (can be plain-text or HTML) b, _ := io.ReadAll(p.Body) log.Printf("Inline text: %v", string(b)) case *mail.AttachmentHeader: // This is an attachment filename, _ := h.Filename() log.Printf("Attachment: %v", filename) } } if err := fetchCmd.Close(); err != nil { log.Fatalf("FETCH command failed: %v", err) } } func ExampleClient_Search() { var c *imapclient.Client data, err := c.UIDSearch(&imap.SearchCriteria{ Body: []string{"Hello world"}, }, nil).Wait() if err != nil { log.Fatalf("UID SEARCH command failed: %v", err) } log.Fatalf("UIDs matching the search criteria: %v", data.AllUIDs()) } func ExampleClient_Idle() { options := imapclient.Options{ UnilateralDataHandler: &imapclient.UnilateralDataHandler{ Expunge: func(seqNum uint32) { log.Printf("message %v has been expunged", seqNum) }, Mailbox: func(data *imapclient.UnilateralDataMailbox) { if data.NumMessages != nil { log.Printf("a new message has been received") } }, }, } c, err := imapclient.DialTLS("mail.example.org:993", &options) if err != nil { log.Fatalf("failed to dial IMAP server: %v", err) } defer c.Close() if err := c.Login("root", "asdf").Wait(); err != nil { log.Fatalf("failed to login: %v", err) } if _, err := c.Select("INBOX", nil).Wait(); err != nil { log.Fatalf("failed to select INBOX: %v", err) } // Start idling idleCmd, err := c.Idle() if err != nil { log.Fatalf("IDLE command failed: %v", err) } defer idleCmd.Close() done := make(chan error, 1) go func() { done <- idleCmd.Wait() }() // Wait for 30s to receive updates from the server, then stop idling t := time.NewTimer(30 * time.Second) defer t.Stop() select { case <-t.C: if err := idleCmd.Close(); err != nil { log.Fatalf("failed to stop idling: %v", err) } if err := <-done; err != nil { log.Fatalf("IDLE command failed: %v", err) } case err := <-done: if err != nil { log.Fatalf("IDLE command failed: %v", err) } } } func ExampleClient_Authenticate_oauth() { var ( c *imapclient.Client username string token string ) if !c.Caps().Has(imap.AuthCap(sasl.OAuthBearer)) { log.Fatal("OAUTHBEARER not supported by the server") } saslClient := sasl.NewOAuthBearerClient(&sasl.OAuthBearerOptions{ Username: username, Token: token, }) if err := c.Authenticate(saslClient); err != nil { log.Fatalf("authentication failed: %v", err) } } func ExampleClient_Closed() { c, err := imapclient.DialTLS("mail.example.org:993", nil) if err != nil { log.Fatalf("failed to dial IMAP server: %v", err) } selected := false go func(c *imapclient.Client) { if err := c.Login("root", "asdf").Wait(); err != nil { log.Fatalf("failed to login: %v", err) } if _, err := c.Select("INBOX", nil).Wait(); err != nil { log.Fatalf("failed to select INBOX: %v", err) } selected = true c.Close() }(c) // This channel shall be closed when the connection is closed. <-c.Closed() log.Println("Connection has been closed") if !selected { log.Fatalf("Connection was closed before selecting mailbox") } }