Giter Club home page Giter Club logo

go-imap-idle's Introduction

go-imap-idle

GoDoc

IDLE extension for go-imap.

This extension has been merged into go-imap. Use built-in support instead of this repository!

Usage

Client

// Let's assume c is an IMAP client
var c *client.Client

// Select a mailbox
if _, err := c.Select("INBOX", false); err != nil {
	log.Fatal(err)
}

idleClient := idle.NewClient(c)

// Create a channel to receive mailbox updates
updates := make(chan client.Update)
c.Updates = updates

// Start idling
done := make(chan error, 1)
go func() {
	done <- idleClient.IdleWithFallback(nil, 0)
}()

// Listen for updates
for {
	select {
	case update := <-updates:
		log.Println("New update:", update)
	case err := <-done:
		if err != nil {
			log.Fatal(err)
		}
		log.Println("Not idling anymore")
		return
	}
}

Server

s.Enable(idle.NewExtension())

License

MIT

go-imap-idle's People

Contributors

emersion avatar fastred avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

go-imap-idle's Issues

Can't make it work withe example code

I'm using the following code:

package main

import (
	"log"
	"os"

	"github.com/emersion/go-imap"
	"github.com/emersion/go-imap-idle"
	"github.com/emersion/go-imap/client"
)

const (
	address  = "foo"
	username = "bar"
	password = "baz"
	mailbox  = "INBOX"
)

func main() {
	c, _ := client.DialTLS(address, nil)
	c.SetDebug(os.Stdout)

	c.Login(username, password)
	c.Select(mailbox, true)

	statuses := make(chan *imap.MailboxStatus)
	c.MailboxUpdates = statuses

	idleClient := idle.NewClient(c)

	stop := make(chan struct{})
	done := make(chan error, 1)

	go func() {
		done <- idleClient.Idle(stop)
	}()

	for {
		select {
		case status := <-statuses:
			log.Println("New mailbox status:", status)
			close(stop)
		case err := <-done:
			if err != nil {
				log.Fatal(err)
			}
			log.Println("Not idling anymore")
			return
		}
	}
}

I run this code and mark one of the messages in INBOX as unread. This results in the following output:

zg9unQ LOGIN bar baz
G_2kiA EXAMINE INBOX
* FLAGS (\Answered \Flagged \Deleted \Seen \Draft unknown-2 unknown-5 unknown-1 unknown-6 unknown-8 unknown-0 $NotJunk $Junk JunkRecorded unknown-7 NotJunk $Forwarded $MDNSent)
* OK [PERMANENTFLAGS ()] Read-only mailbox.
* 14570 EXISTS
* 0 RECENT
* OK [UIDVALIDITY 1394450969] UIDs valid
* OK [UIDNEXT 25420] Predicted next UID
* OK [HIGHESTMODSEQ 59666] Highest
G_2kiA OK [READ-ONLY] Examine completed (0.000 + 0.000 secs).
imap/client: 2016/11/03 09:09:31 response has not been handled: &{* OK HIGHESTMODSEQ [59666] Highest}
tVJIGQ IDLE
+ idling
* 14570 FETCH (FLAGS ())

The line * 14570 FETCH (FLAGS ()) is what is printed when I mark the message as unread. But no code inside of the for loop is executed.

Determine Update Type

Hello,
is there a way to know whether my update object is a MailBox, Expunge or Messages update ?

Closing the stop channel does not close IDLE connection

Issue

I am running into an issue with the package where on closing the done channel the client does not actually close the IDLE connection.

What I'm trying to achieve:

  1. Connect to server
  2. Select Mailbox
  3. IDLE and wait for event
  4. On event exit IDLE (the example should exit when IDLE is stopped, however in reality I would do some processing)

Expected:

Using the code sample below, here is my expected output:

2019/11/26 11:24:05 Connecting to server...
2019/11/26 11:24:05 Connected
2019/11/26 11:24:05 Logged in
2019/11/26 11:24:05 Selected mailbox
2019/11/26 11:24:05 Staring Client
2019/11/26 11:24:35 New update: &{0xc000446180}
2019/11/26 11:24:35 Attempting Stop
2019/11/26 11:24:35 Rec'd done msg
2019/11/26 11:24:35 Not idling anymore

Actual

This is the actual output:

2019/11/26 11:24:05 Connecting to server...
2019/11/26 11:24:05 Connected
2019/11/26 11:24:05 Logged in
2019/11/26 11:24:05 Selected mailbox
2019/11/26 11:24:05 Staring Client
2019/11/26 11:24:35 New update: &{0xc000446180}
2019/11/26 11:24:35 Attempting Stop
(idle updates are still sent)
2019/11/26 11:24:40 New update: &{0xc0000be180}

Additionally, this is the debug data generated from c.SetDebug(f):

5wEDLA LOGIN "username" "pass****"
yOWDJA SELECT INBOX
* 1 EXISTS
* 0 RECENT
* FLAGS (\Seen \Answered \Flagged \Deleted \Draft $MDNSent)
* OK [PERMANENTFLAGS (\Seen \Answered \Flagged \Deleted \Draft $MDNSent)] Permanent flags
* OK [UNSEEN 1] Is the first unseen message
* OK [UIDVALIDITY 14] UIDVALIDITY value
* OK [UIDNEXT 14] The next unique identifier value
yOWDJA OK [READ-WRITE] SELECT completed.
KKDdoA CAPABILITY
* CAPABILITY IMAP4 IMAP4rev1 AUTH=PLAIN AUTH=NTLM AUTH=GSSAPI UIDPLUS MOVE ID XPROXY3 CHILDREN IDLE NAMESPACE LITERAL+
KKDdoA OK CAPABILITY completed.
5ZjbFA IDLE
+ IDLE accepted, awaiting DONE command.
* 1 FETCH (FLAGS (\Seen))
(idle is attempted to be stopped here, however there is no DONE command, IDLE events continue...)
* 1 FETCH (FLAGS ())

I'm not sure if this is my error, or an error of the library as when doing some rudimentary debugging I couldn't see the STOP command being passed to the client objects Execute function.

I'd appreciate any assistance.

Source:

Adapted from the example:

func main() {
	log.Println("Connecting to server...")

	f, err := os.Create("debug.log")
	if err != nil {
		log.Fatalln(err)
	}

	// Connect to server
	c, err := client.DialTLS("exchange.example.com:993", nil)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("Connected")

	c.SetDebug(f)

	// Don't forget to logout
	defer c.Logout()

	// Login
	if err := c.Login("username", "pass****"); err != nil {
		log.Fatal(err)
	}
	log.Println("Logged in")

	// Select a mailbox
	if _, err := c.Select("INBOX", false); err != nil {
		log.Fatal(err)
	}
	log.Println("Selected mailbox")

	idleClient := idle.NewClient(c)

	// Create a channel to receive mailbox updates
	updates := make(chan client.Update)
	c.Updates = updates

	// Check support for the IDLE extension
	if ok, err := idleClient.SupportIdle(); err == nil && ok {
		// Start idling
		stopped := false
		stop := make(chan struct{})
		done := make(chan error, 1)
		go func() {
			log.Println("Staring Client")
			done <- idleClient.Idle(stop)
			log.Println("idle stopped")
		}()

		// Listen for updates
		for {
			select {
			case update := <-updates:
				log.Println("New update:", update)
				if !stopped {
					log.Println("Attempting Stop")
					close(stop)
					stopped = true
				}
			case err := <-done:
				log.Println("Rev'd done msg")
				if err != nil {
					log.Fatal(err)
				}
				log.Println("Not idling anymore")
				return
			}
		}
	} else {
		// Fallback: call periodically c.Noop()
	}
}

Is the done channel closed internally?

Hi,
Does the client.Idle method close the done channel internally?I am trying to close it so that I can logout but I end up with a "close of closed channel" panic.

always Not idling anymore


func main() {
	log.Println("Connecting to server...")

	// Connect to server
	c, err := client.DialTLS("imap.qq.com:993", nil)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("Connected")

	// Don't forget to logout
	defer c.Logout()

	// Login
	if err := c.Login("****@qq.com", "***"); err != nil {
		log.Fatal(err)
	}
	log.Println("Logged in")

	// List mailboxes
	mailboxes := make(chan *imap.MailboxInfo, 10)
	done := make(chan error, 1)
	go func() {
		done <- c.List("", "*", mailboxes)
	}()

	log.Println("Mailboxes:")
	for m := range mailboxes {
		log.Println("*== " + m.Name)
	}

	if err := <-done; err != nil {
		log.Fatal(err)
	}

	// Select INBOX
	mbox, err := c.Select("INBOX", false)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("Flags for INBOX:", mbox.Flags)
	// getMail2(c)

	idleClient := idle.NewClient(c)
	// Create a channel to receive mailbox updates
	updates := make(chan client.Update)
	c.Updates = updates

	// Check support for the IDLE extension
	if ok, err := idleClient.SupportIdle(); err == nil && ok {
		// Start idling
		stopped := false
		stop := make(chan struct{})
		done := make(chan error, 1)
		go func() {
			done <- idleClient.Idle(stop)
		}()

		// Listen for updates
		for {
			select {
			case update := <-updates:
				//https://github.com/emersion/go-imap-idle/issues/11
				log.Println("New update:", update, &update)
				log.Println(reflect.TypeOf(update).String()) //Tell you what type you have
				switch update.(type) {
				case *client.MessageUpdate:
					log.Println("Found message update type")
					msg, _ := update.(*client.MessageUpdate) //This prints what you want here
					log.Println(msg.Message)
				case *client.MailboxUpdate:
					log.Println("Found mailbox update type")
					mbx, _ := update.(*client.MailboxUpdate)
					log.Println(mbx.Mailbox)
					log.Println("UnseenSeqNum:", mbx.Mailbox.UnseenSeqNum) //邮箱中第一封未读邮件的序列号。
					log.Println("Messages:", mbx.Mailbox.Messages)         //此邮箱中的邮件数。
					log.Println("Recent:", mbx.Mailbox.Recent)             //自上次打开邮箱以来未看到的邮件数。
					log.Println("UidNext:", mbx.Mailbox.UidNext)           //

					log.Println("UidValidity:", mbx.Mailbox.UidValidity) //与UID一起,它是消息的唯一标识符。必须大于或等于1。
					// getMail(c)
					go sendWXworkMsg()

					// lib.FetchLast(c, mbx.Mailbox)
					//etc....
				default:
					log.Println("skipping update")
				}

				if !stopped {
					close(stop)
					stopped = true
				}
			case err := <-done:
				if err != nil {
					log.Fatal(err)
				}
				log.Println("Not idling anymore,should restart?")
				return
			}
		}
	} else {
		// Fallback: call periodically c.Noop()
	}

	log.Println("Done!")
}

always Not idling anymore

2020/10/20 09:47:30 Connecting to server...
2020/10/20 09:47:30 Connected
2020/10/20 09:47:30 Logged in
2020/10/20 09:47:30 Mailboxes:
2020/10/20 09:47:31 *== 其他文件夹
2020/10/20 09:47:31 *== INBOX
2020/10/20 09:47:31 *== Sent Messages
2020/10/20 09:47:31 *== Drafts
2020/10/20 09:47:31 *== Deleted Messages
2020/10/20 09:47:31 *== Junk
2020/10/20 09:47:31 *== 其他文件夹/[email protected]
2020/10/20 09:47:31 Flags for INBOX: [\Answered \Flagged \Deleted \Draft \Seen]
2020/10/20 09:47:54 New update: &{0xc00015a000} 0xc000115620
2020/10/20 09:47:54 *client.MailboxUpdate
2020/10/20 09:47:54 Found mailbox update type
2020/10/20 09:47:54 &{INBOX false map[MESSAGES: RECENT: UIDNEXT: UIDVALIDITY:] {0 0} [\Answered \Flagged \Deleted \Draft \Seen] [* \Answered \Flagged \Deleted \Draft \Seen] 59 105 0 0 4237 1413109674}
2020/10/20 09:47:54 UnseenSeqNum: 59
2020/10/20 09:47:54 Messages: 105
2020/10/20 09:47:54 Recent: 0
2020/10/20 09:47:54 UidNext: 4237
2020/10/20 09:47:54 UidValidity: 1413109674
2020/10/20 09:47:54 Not idling anymore,should restart?

not working

I followed the idle client demo, It does't work, nothing happened when new email comes.
I used a python client, which works properly.

Deadlock with Idle in loop

When you execute idle/examine in loop the command examine freezes

To reproduce this error, execute this sequence.

  1. Select a folder (EXAMINE)
  2. Execute idle (IDLE)
  3. When receive new status or timeout terminate idle (DONE)
  4. close folder (CLOSE)
  5. Select a folder (EXAMINE) <--- block here

I tested on Dovecot and Outlook server

full code

package main

import (
	"log"
	"time"

	imap "github.com/emersion/go-imap"
	idle "github.com/emersion/go-imap-idle"
	"github.com/emersion/go-imap/client"
)

const (
	server   = "****:993"
	username = "***@****"
	password = "*****"
)

func main() {
	// Connect to server
	c, err := client.DialTLS(server, nil)
	if err != nil {
		log.Fatal(err)
	}

	log.Println("Connected")

	c.SetDebug(&logger{})

	// Login
	if err := c.Login(username, password); err != nil {
		log.Fatal(err)
	}

	// Don't forget to logout
	defer c.Logout()

	log.Println("Logged in")

	for {
		idleMail(c)
		// do something
		time.Sleep(2 * time.Second)
	}
}

func idleMail(c *client.Client) {
	// Examine INBOX
	if _, err := c.Select("INBOX", true); err != nil {
		log.Fatal(err)
	}

	// Close mailbox
	defer c.Close()

	idleClient := idle.NewClient(c)

	// Create a channel to receive mailbox updates
	statuses := make(chan *imap.MailboxStatus)
	c.MailboxUpdates = statuses

	// Start idling
	stop := make(chan struct{})
	done := make(chan error, 1)

	closed := false
	closeChannel := func() {
		if !closed {
			close(stop)
			closed = true
		}
	}

	go func() {
		done <- idleClient.Idle(stop)
	}()

	// Reset after 30 seconds
	reset := time.After(30 * time.Second)

	// Listen for updates
	for {
		select {
		case status := <-statuses:
			log.Println("New mailbox status:", status)
			closeChannel()
		case err := <-done:
			if err != nil {
				log.Fatal(err)
			}
			log.Println("Not idling anymore")
			return
		case <-reset:
			log.Println("Timeout")
			closeChannel()
		}
	}
}

type logger struct{}

func (l *logger) Write(p []byte) (int, error) {
	log.Println("[IMAP]", string(p))
	return len(p), nil
}

Log

2017/06/21 20:26:20 Connected
2017/06/21 20:26:20 [IMAP] oR174Q LOGIN *****@***** *****

2017/06/21 20:26:20 Logged in
2017/06/21 20:26:20 [IMAP] dhVfMg EXAMINE INBOX

2017/06/21 20:26:20 [IMAP] * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
* OK [PERMANENTFLAGS ()] Read-only mailbox.
* 3749 EXISTS
* 0 RECENT
* OK [UNSEEN 3748] First unseen.
* OK [UIDVALIDITY 1416039410] UIDs valid
* OK [UIDNEXT 3751] Predicted next UID
* OK [HIGHESTMODSEQ 3799] Highest
dhVfMg OK [READ-ONLY] Examine completed (0.000 + 0.000 secs).

imap/client: 2017/06/21 20:26:20 response has not been handled: &{* OK HIGHESTMODSEQ [3799] Highest}
2017/06/21 20:26:20 [IMAP] KQrCWA IDLE

2017/06/21 20:26:20 [IMAP] + idling

2017/06/21 20:26:50 Timeout
2017/06/21 20:26:50 [IMAP] DONE

2017/06/21 20:26:50 [IMAP] KQrCWA OK Idle completed (0.001 + 30.001 + 30.001 secs).

2017/06/21 20:26:50 Not idling anymore
2017/06/21 20:26:50 [IMAP] Fij9eA CLOSE

2017/06/21 20:26:50 [IMAP] Fij9eA OK Close completed (0.000 + 0.000 secs).

2017/06/21 20:26:52 [IMAP] -DzrKA EXAMINE INBOX

2017/06/21 20:26:52 [IMAP] * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
* OK [PERMANENTFLAGS ()] Read-only mailbox.
* 3749 EXISTS
* 0 RECENT
* OK [UNSEEN 3748] First unseen.
* OK [UIDVALIDITY 1416039410] UIDs valid
* OK [UIDNEXT 3751] Predicted next UID
* OK [HIGHESTMODSEQ 3799] Highest
-DzrKA OK [READ-ONLY] Examine completed (0.000 + 0.000 secs).

Quit

^\SIGQUIT: quit
PC=0x456301 m=0 sigcode=128

goroutine 0 [idle]:
runtime.futex(0x6e17b0, 0x0, 0x0, 0x0, 0x7fbc00000000, 0x27a8bc0, 0x0, 0x0, 0x7fff027a8bf0, 0x40e8cb, ...)
        /usr/local/go/src/runtime/sys_linux_amd64.s:422 +0x21
runtime.futexsleep(0x6e17b0, 0x0, 0xffffffffffffffff)
        /usr/local/go/src/runtime/os_linux.go:45 +0x62
runtime.notesleep(0x6e17b0)
        /usr/local/go/src/runtime/lock_futex.go:145 +0x6b
runtime.stopm()
        /usr/local/go/src/runtime/proc.go:1650 +0xad
runtime.findrunnable(0xc420023300, 0x0)
        /usr/local/go/src/runtime/proc.go:2102 +0x2e4
runtime.schedule()
        /usr/local/go/src/runtime/proc.go:2222 +0x14c
runtime.park_m(0xc42016d1e0)
        /usr/local/go/src/runtime/proc.go:2285 +0xab
runtime.mcall(0x7fff027a8d80)
        /usr/local/go/src/runtime/asm_amd64.s:269 +0x5b

goroutine 1 [select, 5 minutes]:
mmtest/vendor/github.com/emersion/go-imap/client.(*Client).execute(0xc42009a0a0, 0x6c4aa0, 0xc42013ca20, 0x6c4ae0, 0xc4
2046e020, 0x10002, 0x200000001, 0xc4200b7cd0)
        /go/src/mmtest/vendor/github.com/emersion/go-imap/client/client.go:165 +0x63b
mmtest/vendor/github.com/emersion/go-imap/client.(*Client).Select(0xc42009a0a0, 0x5f83d2, 0x5, 0xc420011601, 0xc4202266
c0, 0x0, 0x0)
        /go/src/mmtest/vendor/github.com/emersion/go-imap/client/cmd_auth.go:36 +0x160
main.idleMail(0xc42009a0a0)
        /go/src/mmtest/teste/teste.go:48 +0x5d
main.main()
        /go/src/mmtest/teste/teste.go:40 +0x2a9

goroutine 17 [syscall, 5 minutes, locked to thread]:
runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:2197 +0x1

goroutine 18 [chan receive, 5 minutes]:
mmtest/vendor/github.com/emersion/go-imap/client.(*Client).handleContinuationReqs(0xc42009a0a0, 0xc42037b920)
        /go/src/mmtest/vendor/github.com/emersion/go-imap/client/client.go:228 +0xea
created by mmtest/vendor/github.com/emersion/go-imap/client.New
        /go/src/mmtest/vendor/github.com/emersion/go-imap/client/client.go:446 +0x36b

goroutine 19 [chan send, 5 minutes]:
mmtest/vendor/github.com/emersion/go-imap/client.(*Client).handleUnilateral(0xc42009a0a0)
        /go/src/mmtest/vendor/github.com/emersion/go-imap/client/client.go:352 +0xc39
created by mmtest/vendor/github.com/emersion/go-imap/client.New
        /go/src/mmtest/vendor/github.com/emersion/go-imap/client/client.go:447 +0x38d

goroutine 20 [chan send, 5 minutes]:
mmtest/vendor/github.com/emersion/go-imap.(*MultiRespHandler).HandleFrom(0xc42010ed50, 0xc42037b9e0, 0xc4201e47d0, 0x0)
        /go/src/mmtest/vendor/github.com/emersion/go-imap/handle.go:77 +0x16e
created by mmtest/vendor/github.com/emersion/go-imap/client.New
        /go/src/mmtest/vendor/github.com/emersion/go-imap/client/client.go:448 +0x3bc

goroutine 34 [chan receive, 5 minutes]:
mmtest/vendor/github.com/emersion/go-imap/client.(*Client).read(0xc42009a0a0, 0xc4201ba060, 0x0, 0x0)
        /go/src/mmtest/vendor/github.com/emersion/go-imap/client/client.go:116 +0x36c
created by mmtest/vendor/github.com/emersion/go-imap/client.(*Client).handleUnilateral
        /go/src/mmtest/vendor/github.com/emersion/go-imap/client/client.go:261 +0x100

goroutine 27 [chan receive, 5 minutes]:
mmtest/vendor/github.com/emersion/go-imap/responses.(*Select).HandleFrom(0xc42046e020, 0xc420226ae0, 0x0, 0x0)
        /go/src/mmtest/vendor/github.com/emersion/go-imap/responses/select.go:21 +0xe3
mmtest/vendor/github.com/emersion/go-imap/client.(*Client).execute.func2(0xc420226b40, 0x6c4ae0, 0xc42046e020, 0xc42022
6ae0)
        /go/src/mmtest/vendor/github.com/emersion/go-imap/client/client.go:160 +0x3b
created by mmtest/vendor/github.com/emersion/go-imap/client.(*Client).execute
        /go/src/mmtest/vendor/github.com/emersion/go-imap/client/client.go:161 +0x6ff

rax    0xca
rbx    0x0
rcx    0x456303
rdx    0x0
rdi    0x6e17b0
rsi    0x0
rbp    0x7fff027a8bc0
rsp    0x7fff027a8b78
r8     0x0
r9     0x0
r10    0x0
r11    0x286
r12    0x0
r13    0xc42016c4e0
r14    0x43deb0
r15    0x6497f8
rip    0x456301
rflags 0x286
cs     0x33
fs     0x0
gs     0x0
exit status 2

go-imap v2: Add IdleBackend

With go-imap v2 backend changes, updates are no longer sent directly but instead may be held back before command is sent.

IDLE is special in this case as it allows updates to be sent immediately but in current implementation backend does not know it can do so.

Something like that will work just fine I suppose:

type IdleBackend interface {
  Idle(done <-chan struct{})
}

Logout in idle fail

@emersion
I don't know why, but sometimes Client.Logout() doesn't work with Idle extension.
I use new connection (method Connect) for every single request (REST API).
Methods like Fetch, Flags, Copy etc. (without idle) work.
I use websocket to notify user about new emails. Response on connecting:

ycfdMg SELECT INBOX
imap/client: 2017/02/22 14:22:24 response has not been handled: &{* OK NOMODSEQ [] No permanent modsequences}
pKKeHQ IDLE
+ idling

And when user close connection if works:

bIjzWw LOGOUT
DONE
 LOGOUT
DONE
pKKeHQ BAD Expected DONE.
* BYE Logging out
bIjzWw OK Logout completed.

Or if logout fails:

lbNHAg LOGOUT
DONE
6b7TDA BAD Expected DONE.
DONE BAD Error in IMAP command : Unknown command.
imap/client: 2017/02/22 14:24:11 response has not been handled: &{DONE BAD  [] Error in IMAP command : Unknown command.}

type Email struct {
  Client *client.Client
  DB     *sql.DB
}

func (e *Email) Connect(data){
   // ... connect to server, fill Client and db
}

func (e *Email) Idle(email string, broadcastToUser chan<- broadcast.Message,
  finish chan bool, errors chan error) {

  defer e.Client.Logout()

  fmt.Println(e.Client.State)

  _, err := e.Client.Select("INBOX", false)
  if err != nil {
    errors <- err
    return
  }

  idleClient := idle.NewClient(e.Client)

  statuses := make(chan *imap.MailboxStatus)
  e.Client.MailboxUpdates = statuses

  //start idling
  stop := make(chan struct{})
  done := make(chan error, 1)
  go func() {
    done <- idleClient.Idle(stop)
  }()

  // listen for updates
  for {
    select {

    case status := <-statuses:
      broadcastToUser <- broadcast.Message{
        Email: email,
        Msg: map[string]interface{}{
          "action": "newmessage",
        },
      }

    case err = <-done:
      if err != nil {
        close(stop)
        errors <- err
        return
      }

    case <-finish:
      close(stop)
      return
    }

  }
}

How to get updated message?

Hello,

I ran code from example, sent mail to myself and got message in console: "New update: &{0xc000114000}". What is this uid? Is it possible to get a message by this number?

Microsoft Exchange Server 2016 IMAP4 - panic: close of closed channel

Error panic: close of closed channel using outlook.com

This error occurs when:
1 - statuses receive an mailbox update
2 - close stop channel
3 - statuses receive another mailbox update
4 - try to close stop channel again

(example code)

// Create a channel to receive mailbox updates
statuses := make(chan *imap.MailboxStatus)
c.MailboxUpdates = statuses

// Check support for the IDLE extension
if ok, err := idleClient.SupportIdle(); err == nil && ok {
	// Start idling
	stop := make(chan struct{})
	done := make(chan error, 1)
	go func() {
		done <- idleClient.Idle(stop)
	}()

	// Listen for updates
	for {
		select {
		case status := <-statuses:
			log.Println("New mailbox status:", status)
			close(stop)
		case err := <-done:
			if err != nil {
				log.Fatal(err)
			}
			log.Println("Not idling anymore")
			return
		}
	}
}

(debug)

yPIv3g IDLE

+ IDLE accepted, awaiting DONE command.

* 1 RECENT
* 7 EXISTS

New mailbox status: [UNSEEN 5 UIDVALIDITY 14 UIDNEXT 51 MESSAGES 6 RECENT 1 FLAGS <nil> PERMANENTFLAGS <nil>]
New mailbox status: [UIDVALIDITY 14 UIDNEXT 51 MESSAGES 7 RECENT 1 FLAGS <nil> PERMANENTFLAGS <nil> UNSEEN 5]
Logout
DONE

IVHCMA LOGOUT
GOUT

yPIv3g OK IDLE completed.

imap/client: 2017/06/09 15:53:46 response has not been handled: &{yPIv3g OK  [] IDLE completed.}
DONE BAD Command Error. 12

imap/client: 2017/06/09 15:53:46 response has not been handled: &{DONE BAD  [] Command Error. 12}
* BYE Microsoft Exchange Server 2016 IMAP4 server signing off.
IVHCMA OK LOGOUT completed.

panic: close of closed channel

Client

Hey,
I can use one client for idle to get notify and imap to read messages?
Thanks.

Connection goes stale.

My idle client has been running for a few days. During that period I might have disconnected from the internet a couple of times. The connection does not timeout or send an error via the done channel and no new messages are received. It works only after I restart it. It seems that the connection went stale. Is there some sort of timeout mechanism I can use to determine when I need to re-establish the connection? I did get a "imap: connection closed" error once but not this time.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.