Beispiel #1
0
func FetchMessages(c *imap.Client, uidSet *imap.SeqSet) (fetched []MsgData, err error) {
	cmd, errF := c.UIDFetch(uidSet, "RFC822")
	if errF != nil {
		err = errF
		return
	}

	for cmd.InProgress() {
		errC := c.Recv(-1)
		if errC != nil {
			return
		}
		for _, rsp := range cmd.Data {

			uid := imap.AsNumber(rsp.MessageInfo().Attrs["UID"])
			mime := imap.AsBytes(rsp.MessageInfo().Attrs["RFC822"])
			msg, errR := mail.ReadMessage(bytes.NewReader(mime))
			if errR != nil {
				continue
			}
			if msg != nil {
				msgdata := GetMessage(msg, uid)
				fetched = append(fetched, msgdata)
			}
		}
		cmd.Data = nil
	}

	return
}
Beispiel #2
0
func FetchAllUIDs(c *imap.Client) (uids []uint32, err error) {
	maxmessages := 150000
	uids = make([]uint32, maxmessages)

	set, errS := imap.NewSeqSet("1:*")
	if errS != nil {
		err = errS
		return
	}

	cmd, errF := c.UIDFetch(set, "RFC822.SIZE")
	if errF != nil {
		err = errF
		return
	}

	messagenum := uint32(0)
	for cmd.InProgress() {
		errC := c.Recv(-1)
		if errC != nil {
			continue
		}
		for _, rsp := range cmd.Data {
			uid := imap.AsNumber(rsp.MessageInfo().Attrs["UID"])
			uids[messagenum] = uid
		}
		cmd.Data = nil
		messagenum++
	}

	uids = uids[:messagenum]
	return
}
Beispiel #3
0
// getEmails will fetch the full bodies of all emails listed in the given command.
func getEmails(client *imap.Client, cmd *imap.Command) ([]map[string]interface{}, error) {
	var emails []map[string]interface{}
	seq := new(imap.SeqSet)
	for _, rsp := range cmd.Data {
		for _, uid := range rsp.SearchResults() {
			seq.AddNum(uid)
		}
	}
	if seq.Empty() {
		return emails, nil
	}
	fCmd, err := imap.Wait(client.UIDFetch(seq, "INTERNALDATE", "BODY[]", "UID", "RFC822.HEADER"))
	if err != nil {
		return emails, err
	}

	var email map[string]interface{}
	for _, msgData := range fCmd.Data {
		msgFields := msgData.MessageInfo().Attrs
		email, err = newEmailMessage(msgFields)
		if err != nil {
			return emails, err
		}
		emails = append(emails, email)

		// mark message as read
		fSeq := new(imap.SeqSet)
		fSeq.AddNum(imap.AsNumber(msgFields["UID"]))
		_, err = imap.Wait(client.UIDStore(fSeq, "+FLAGS", "\\SEEN"))
		if err != nil {
			return emails, err
		}
	}
	return emails, nil
}
Beispiel #4
0
func FetchMessages(c *imap.Client, uidSet *imap.SeqSet) (err error) {
	cmd, err := c.UIDFetch(uidSet, "RFC822")

	for cmd.InProgress() {
		c.Recv(-1)
		for _, rsp := range cmd.Data {
			uid := imap.AsNumber(rsp.MessageInfo().Attrs["UID"])
			mime := imap.AsBytes(rsp.MessageInfo().Attrs["RFC822"])
			if msg, _ := mail.ReadMessage(bytes.NewReader(mime)); msg != nil {
				PrintMessageAsJSON(msg, uid)
			}
		}
		cmd.Data = nil
	}

	return
}
Beispiel #5
0
func (i *ImapSession) Append(folder string, extraCopies []string, msg io.Reader) error {
	c := i.client

	mbox := folder

	buf := new(bytes.Buffer)
	buf.ReadFrom(msg)

	now := time.Now()
	cmd, err := imap.Wait(c.Append(mbox, nil, &now, imap.NewLiteral(buf.Bytes())))
	if err != nil {
		fmt.Println(err)
		return err
	}

	rsp, err := cmd.Result(imap.OK)
	if err != nil {
		fmt.Println(err)
		return err
	}

	uid := imap.AsNumber(rsp.Fields[len(rsp.Fields)-1])

	set, _ := imap.NewSeqSet("")
	set.AddNum(uid)

	imapMust(imap.Wait(c.Select(mbox, false)))

	for _, mb := range extraCopies {
		if i.isGmail {
			imapMust(imap.Wait(c.UIDStore(set, "X-GM-LABELS", imap.NewFlagSet(mb))))
		} else {
			imapMust(c.UIDCopy(set, mb))
		}
	}

	return nil
}
Beispiel #6
0
// Idle setup the processes to wait for notifications from the IMAP source connection.
// If an EXISTS or EXPUNGE command comes across the pipe, the appropriate actions will be
// taken to update the destinations. If the process decides the inboxes are out of sync,
// it will pass a bool to the requestPurge channel. It is expected that the requestPurge
// channel is setup to initiate a purge process when it receives the notificaiton.
func Idle(src *imap.Client, appendRequests []chan WorkRequest, requestPurge chan bool) (err error) {
	var nextUID uint32
	if nextUID, err = getNextUID(src); err != nil {
		log.Printf("Unable to get UIDNext: %s", err.Error())
		return err
	}

	// hold the size so we can determine how to react to commands
	startSize := src.Mailbox.Messages

	// setup interrupt signal channel to terminate the idle
	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt, os.Kill)

	// setup ticker to reset the idle every 20 minutes (RFC-2177 recommends 29 mins max)
	timeout := time.NewTicker(idleTimeoutMinutes * time.Minute)

	// setup poller signal for checking for data on the idle command
	poll := make(chan bool, 1)
	poll <- true

	log.Print("beginning idle...")
	_, idleErr := src.Idle()
	if (idleErr != nil) && (idleErr != imap.ErrTimeout) {
		log.Printf("Idle error: %s", idleErr.Error())
		return
	}

	for {

		select {
		// if we receive a 'poll' we should check the pipe for new messages
		case <-poll:

			err = src.Recv(0)
			if (idleErr != nil) && (idleErr != imap.ErrTimeout) {
				log.Printf("Idle error: %s", idleErr.Error())
				go sleep(poll)
				continue
			}

			// cache the data so we dont mess it up while start/stopping idle
			var tempData []*imap.Response
			tempData = append(tempData, src.Data...)
			src.Data = nil
			for _, data := range tempData {
				switch data.Type {
				case imap.Data:
					// len of 2 likely means its an EXPUNGE or EXISTS command...
					if len(data.Fields) == 2 {
						msgNum := imap.AsNumber(data.Fields[0])

						switch data.Fields[1] {
						case "EXPUNGE":
							log.Printf("Received an EXPUNGE notification requesting purge - %d", msgNum)
							startSize = msgNum
							requestPurge <- true

						case "EXISTS":
							log.Printf("Received an EXISTS notification - %d", msgNum)
							if startSize > msgNum {
								log.Printf("Mailbox decreased in size %d --> %d. Requesting a purge. MAILBOX MAY NEED TO SYNC", startSize, msgNum)
								requestPurge <- true
								startSize = msgNum
								continue
							}

							// temporarily term the idle so we can fetch the message
							if _, err = src.IdleTerm(); err != nil {
								log.Printf("error while temporarily terminating idle: %s", err.Error())
								return
							}
							log.Printf("terminated idle. appending message.")

							newMessages := msgNum - startSize
							log.Printf("attempting to find/append %d new messages", newMessages)
							for i := uint32(0); i < newMessages; i++ {
								var request WorkRequest
								if request, err = getMessageInfo(src, nextUID); err == nil {

									log.Printf("creating %d append requests for %d", len(appendRequests), nextUID)
									for _, requests := range appendRequests {
										requests <- request
									}
									log.Printf("done creating append requests for %d", nextUID)
									nextUID++
									startSize++
								} else {
									log.Printf("Unable to find message for UID (%d): %s", nextUID, err.Error())
								}
							}

							log.Printf("continuing idle...")
							// turn idle back on
							if _, err = src.Idle(); err != nil {
								log.Printf("Unable to restart idle: %s", err.Error())
								return
							}
						}
					}
				}
			}

			go sleep(poll)

		case <-interrupt:
			log.Printf("Received interrupt. Terminating idle...")
			_, err = src.IdleTerm()
			if err != nil {
				log.Printf("error while terminating idle: %s", err.Error())
			}
			return
		case <-timeout.C:
			log.Printf("resetting idle...")
			_, err = src.IdleTerm()
			if err != nil {
				log.Printf("error while temporarily terminating idle: %s", err.Error())
				return
			}
			log.Printf("terminated idle.")

			// turn idle back on
			_, err = src.Idle()
			if err != nil {
				log.Printf("Unable to restart idle: %s", err.Error())
				return
			}
			log.Printf("idle restarted.")
		}
	}

	return
}