diff --git a/idle.go b/idle.go new file mode 100644 index 0000000..9746e09 --- /dev/null +++ b/idle.go @@ -0,0 +1,27 @@ +package imap + +type ExistsEvent struct { + MessageIndex int +} + +type ExpungeEvent struct { + MessageIndex int +} + +type FetchEvent struct { + MessageIndex int + UID uint32 + Flags []string +} + +type IdleHandler struct { + OnExists func(event ExistsEvent) + OnExpunge func(event ExpungeEvent) + OnFetch func(event FetchEvent) +} + +const ( + IdleEventExists = "EXISTS" + IdleEventExpunge = "EXPUNGE" + IdleEventFetch = "FETCH" +) diff --git a/main.go b/main.go index e985492..b76e4af 100644 --- a/main.go +++ b/main.go @@ -436,6 +436,68 @@ func (d *Dialer) GetFolders() (folders []string, err error) { return folders, nil } +func (d *Dialer) runIdleEvent(data []byte, handler *IdleHandler) error { + index := 0 + event := "" + if _, err := fmt.Sscanf(string(data), "%d %s", &index, &event); err != nil { + return fmt.Errorf("invalid IDLE event format: %s", data) + } + switch event { + case IdleEventExists: + if handler.OnExists != nil { + handler.OnExists(ExistsEvent{MessageIndex: index}) + + } + case IdleEventExpunge: + if handler.OnExpunge != nil { + handler.OnExpunge(ExpungeEvent{MessageIndex: index}) + } + case IdleEventFetch: + if handler.OnFetch == nil { + return nil + } + str := `9 FETCH (UID 64 FLAGS (\Seen \Answered \Flagged))` + re := regexp.MustCompile(`(\d+) FETCH \(UID (\d+) FLAGS \((.*)\)\)`) + matches := re.FindStringSubmatch(str) + if len(matches) == 4 { + messageIndex, _ := strconv.Atoi(matches[1]) + uid, _ := strconv.Atoi(matches[2]) + flags := strings.FieldsFunc(strings.ReplaceAll(matches[3], `\`, ""), func(r rune) bool { + return unicode.IsSpace(r) || r == ',' + }) + handler.OnFetch(FetchEvent{MessageIndex: messageIndex, UID: uint32(uid), Flags: flags}) + } else { + return fmt.Errorf("invalid FETCH event format: %s", data) + } + } + + return nil +} + +func (d *Dialer) StartIdle(handler *IdleHandler) error { + done := make(chan error, 1) + go func() { + defer close(done) + if _, err := d.Exec("IDLE", true, 0, func(line []byte) (err error) { + if bytes.HasPrefix(line, []byte("* ")) { + err := d.runIdleEvent(line[2:], handler) + if err != nil { + fmt.Println(err) + return err + } + + } else if bytes.Equal(line, []byte("OK IDLE terminated")) { + done <- nil + } + return + }); err != nil { + done <- err + return + } + }() + return nil +} + var regexExists = regexp.MustCompile(`\*\s+(\d+)\s+EXISTS`) // GetTotalEmailCount returns the total number of emails in every folder