2018-01-10 02:39:00 +01:00
|
|
|
package imap
|
|
|
|
|
|
|
|
import (
|
2018-01-14 11:30:11 +01:00
|
|
|
"fmt"
|
|
|
|
"net/url"
|
2021-12-07 00:05:36 +01:00
|
|
|
"time"
|
2018-01-10 17:19:45 +01:00
|
|
|
|
2018-01-14 11:30:11 +01:00
|
|
|
"github.com/emersion/go-imap"
|
2020-09-12 15:05:02 +02:00
|
|
|
sortthread "github.com/emersion/go-imap-sortthread"
|
2018-02-01 03:18:21 +01:00
|
|
|
"github.com/emersion/go-imap/client"
|
2022-02-12 23:08:18 +01:00
|
|
|
"github.com/pkg/errors"
|
imap: add option to cache headers
Add option to cache headers for imap accounts. Cache db is located at
$XDG_CACHE_DIR/aerc/{account name}. The cache is cleaned of stale
entries when aerc is first opened.
Two new account level configuration options are introduced:
* cache-headers (Default: false)
* cache-max-age (Default: 30 days (720 hours))
The change in worker/imap/open.go is to set the selected directory. This
is required to access the UIDVALIDITY field, which is used in
combination with the message ID to form the key for use in the cache db.
The key structure is: "header.{UIDVALIDITY}.{UID}"
Where reasonable, cache does not stop aerc from running. In general, if
there is an error in the cache, aerc should continue working as usual.
Errors are either displayed to the user or logged.
All messages are stored without flags, and when retrieved have the flags
set to SEEN. This is to prevent UI flashes. A new method to
FetchMessageFlags is introduced to update flags of cached headers. This
is done asynchronously, and the user will see their messages appear and
then any flags updated. The message will initially show as SEEN, but
will update to unread. I considered updating the cache with the
last-known flag state, however it seems prudent to spare the R/W cycle
and assume that - eventually - all messages will end up read, and if it
isn't the update will occur rather quickly.
Note that leveldb puts a lock on the database, preventing multiple
instances of aerc from accessing the cache at the same time.
Much of this work is based on previous efforts by Vladimír Magyar.
Implements: https://todo.sr.ht/~rjarry/aerc/2
Thanks: Vladimír Magyar <vladimir@mgyar.me>
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
Tested-by: inwit <inwit@sindominio.net>
Reviewed-by: Koni Marti <koni.marti@gmail.com>
Acked-by: Robin Jarry <robin@jarry.cc>
2022-06-15 14:23:51 +02:00
|
|
|
"github.com/syndtr/goleveldb/leveldb"
|
2018-02-01 03:18:21 +01:00
|
|
|
|
2021-11-05 10:19:46 +01:00
|
|
|
"git.sr.ht/~rjarry/aerc/lib"
|
2022-07-19 22:31:51 +02:00
|
|
|
"git.sr.ht/~rjarry/aerc/logging"
|
2021-11-05 10:19:46 +01:00
|
|
|
"git.sr.ht/~rjarry/aerc/models"
|
|
|
|
"git.sr.ht/~rjarry/aerc/worker/handlers"
|
|
|
|
"git.sr.ht/~rjarry/aerc/worker/types"
|
2018-01-10 02:39:00 +01:00
|
|
|
)
|
|
|
|
|
2019-07-18 06:25:42 +02:00
|
|
|
func init() {
|
|
|
|
handlers.RegisterWorkerFactory("imap", NewIMAPWorker)
|
|
|
|
handlers.RegisterWorkerFactory("imaps", NewIMAPWorker)
|
|
|
|
}
|
|
|
|
|
2022-04-30 01:08:54 +02:00
|
|
|
var (
|
|
|
|
errUnsupported = fmt.Errorf("unsupported command")
|
2022-04-30 01:08:58 +02:00
|
|
|
errClientNotReady = fmt.Errorf("client not ready")
|
2022-04-30 01:08:54 +02:00
|
|
|
errNotConnected = fmt.Errorf("not connected")
|
|
|
|
errAlreadyConnected = fmt.Errorf("already connected")
|
|
|
|
)
|
2018-01-14 11:30:11 +01:00
|
|
|
|
|
|
|
type imapClient struct {
|
|
|
|
*client.Client
|
2021-11-12 18:12:02 +01:00
|
|
|
thread *sortthread.ThreadClient
|
|
|
|
sort *sortthread.SortClient
|
2018-01-14 11:30:11 +01:00
|
|
|
}
|
|
|
|
|
2022-04-30 01:08:54 +02:00
|
|
|
type imapConfig struct {
|
2022-04-30 01:08:56 +02:00
|
|
|
scheme string
|
|
|
|
insecure bool
|
|
|
|
addr string
|
|
|
|
user *url.Userinfo
|
|
|
|
folders []string
|
|
|
|
oauthBearer lib.OAuthBearer
|
|
|
|
idle_timeout time.Duration
|
2022-04-30 01:08:57 +02:00
|
|
|
idle_debounce time.Duration
|
2022-04-30 01:08:56 +02:00
|
|
|
reconnect_maxwait time.Duration
|
2022-04-30 01:08:54 +02:00
|
|
|
// tcp connection parameters
|
|
|
|
connection_timeout time.Duration
|
|
|
|
keepalive_period time.Duration
|
|
|
|
keepalive_probes int
|
|
|
|
keepalive_interval int
|
imap: add option to cache headers
Add option to cache headers for imap accounts. Cache db is located at
$XDG_CACHE_DIR/aerc/{account name}. The cache is cleaned of stale
entries when aerc is first opened.
Two new account level configuration options are introduced:
* cache-headers (Default: false)
* cache-max-age (Default: 30 days (720 hours))
The change in worker/imap/open.go is to set the selected directory. This
is required to access the UIDVALIDITY field, which is used in
combination with the message ID to form the key for use in the cache db.
The key structure is: "header.{UIDVALIDITY}.{UID}"
Where reasonable, cache does not stop aerc from running. In general, if
there is an error in the cache, aerc should continue working as usual.
Errors are either displayed to the user or logged.
All messages are stored without flags, and when retrieved have the flags
set to SEEN. This is to prevent UI flashes. A new method to
FetchMessageFlags is introduced to update flags of cached headers. This
is done asynchronously, and the user will see their messages appear and
then any flags updated. The message will initially show as SEEN, but
will update to unread. I considered updating the cache with the
last-known flag state, however it seems prudent to spare the R/W cycle
and assume that - eventually - all messages will end up read, and if it
isn't the update will occur rather quickly.
Note that leveldb puts a lock on the database, preventing multiple
instances of aerc from accessing the cache at the same time.
Much of this work is based on previous efforts by Vladimír Magyar.
Implements: https://todo.sr.ht/~rjarry/aerc/2
Thanks: Vladimír Magyar <vladimir@mgyar.me>
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
Tested-by: inwit <inwit@sindominio.net>
Reviewed-by: Koni Marti <koni.marti@gmail.com>
Acked-by: Robin Jarry <robin@jarry.cc>
2022-06-15 14:23:51 +02:00
|
|
|
cacheEnabled bool
|
|
|
|
cacheMaxAge time.Duration
|
2022-04-30 01:08:54 +02:00
|
|
|
}
|
|
|
|
|
2018-01-10 02:39:00 +01:00
|
|
|
type IMAPWorker struct {
|
2022-04-30 01:08:54 +02:00
|
|
|
config imapConfig
|
2018-01-14 11:30:11 +01:00
|
|
|
|
2019-03-11 04:45:00 +01:00
|
|
|
client *imapClient
|
2019-08-22 14:53:27 +02:00
|
|
|
selected *imap.MailboxStatus
|
2019-03-11 04:45:00 +01:00
|
|
|
updates chan client.Update
|
|
|
|
worker *types.Worker
|
2022-06-20 21:49:05 +02:00
|
|
|
seqMap SeqMap
|
2022-04-30 01:08:55 +02:00
|
|
|
|
2022-04-30 01:08:56 +02:00
|
|
|
idler *idler
|
|
|
|
observer *observer
|
imap: add option to cache headers
Add option to cache headers for imap accounts. Cache db is located at
$XDG_CACHE_DIR/aerc/{account name}. The cache is cleaned of stale
entries when aerc is first opened.
Two new account level configuration options are introduced:
* cache-headers (Default: false)
* cache-max-age (Default: 30 days (720 hours))
The change in worker/imap/open.go is to set the selected directory. This
is required to access the UIDVALIDITY field, which is used in
combination with the message ID to form the key for use in the cache db.
The key structure is: "header.{UIDVALIDITY}.{UID}"
Where reasonable, cache does not stop aerc from running. In general, if
there is an error in the cache, aerc should continue working as usual.
Errors are either displayed to the user or logged.
All messages are stored without flags, and when retrieved have the flags
set to SEEN. This is to prevent UI flashes. A new method to
FetchMessageFlags is introduced to update flags of cached headers. This
is done asynchronously, and the user will see their messages appear and
then any flags updated. The message will initially show as SEEN, but
will update to unread. I considered updating the cache with the
last-known flag state, however it seems prudent to spare the R/W cycle
and assume that - eventually - all messages will end up read, and if it
isn't the update will occur rather quickly.
Note that leveldb puts a lock on the database, preventing multiple
instances of aerc from accessing the cache at the same time.
Much of this work is based on previous efforts by Vladimír Magyar.
Implements: https://todo.sr.ht/~rjarry/aerc/2
Thanks: Vladimír Magyar <vladimir@mgyar.me>
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
Tested-by: inwit <inwit@sindominio.net>
Reviewed-by: Koni Marti <koni.marti@gmail.com>
Acked-by: Robin Jarry <robin@jarry.cc>
2022-06-15 14:23:51 +02:00
|
|
|
cache *leveldb.DB
|
2022-07-05 21:48:37 +02:00
|
|
|
|
|
|
|
caps *models.Capabilities
|
2018-01-10 02:39:00 +01:00
|
|
|
}
|
|
|
|
|
2019-07-18 06:25:42 +02:00
|
|
|
func NewIMAPWorker(worker *types.Worker) (types.Backend, error) {
|
2018-01-10 02:39:00 +01:00
|
|
|
return &IMAPWorker{
|
2019-05-14 02:16:55 +02:00
|
|
|
updates: make(chan client.Update, 50),
|
|
|
|
worker: worker,
|
2019-08-22 14:53:27 +02:00
|
|
|
selected: &imap.MailboxStatus{},
|
2022-04-30 01:08:55 +02:00
|
|
|
idler: newIdler(imapConfig{}, worker),
|
2022-04-30 01:08:56 +02:00
|
|
|
observer: newObserver(imapConfig{}, worker),
|
2022-07-05 21:48:37 +02:00
|
|
|
caps: &models.Capabilities{},
|
2019-07-18 06:25:42 +02:00
|
|
|
}, nil
|
2018-01-10 02:39:00 +01:00
|
|
|
}
|
|
|
|
|
2022-04-30 01:08:55 +02:00
|
|
|
func (w *IMAPWorker) newClient(c *client.Client) {
|
|
|
|
c.Updates = w.updates
|
|
|
|
w.client = &imapClient{c, sortthread.NewThreadClient(c), sortthread.NewSortClient(c)}
|
|
|
|
w.idler.SetClient(w.client)
|
2022-04-30 01:08:56 +02:00
|
|
|
w.observer.SetClient(w.client)
|
2022-07-05 21:48:37 +02:00
|
|
|
sort, err := w.client.sort.SupportSort()
|
|
|
|
if err == nil && sort {
|
|
|
|
w.caps.Sort = true
|
2022-07-19 22:31:51 +02:00
|
|
|
logging.Infof("Server Capability found: Sort")
|
2022-07-05 21:48:37 +02:00
|
|
|
}
|
|
|
|
thread, err := w.client.thread.SupportThread()
|
|
|
|
if err == nil && thread {
|
|
|
|
w.caps.Thread = true
|
2022-07-19 22:31:51 +02:00
|
|
|
logging.Infof("Server Capability found: Thread")
|
2022-07-05 21:48:37 +02:00
|
|
|
}
|
2022-04-30 01:08:55 +02:00
|
|
|
}
|
|
|
|
|
2018-01-14 11:30:11 +01:00
|
|
|
func (w *IMAPWorker) handleMessage(msg types.WorkerMessage) error {
|
2022-01-19 13:18:08 +01:00
|
|
|
defer func() {
|
2022-04-30 01:08:55 +02:00
|
|
|
w.idler.Start()
|
2022-01-19 13:18:08 +01:00
|
|
|
}()
|
2022-04-30 01:08:55 +02:00
|
|
|
if err := w.idler.Stop(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var reterr error // will be returned at the end, needed to support idle
|
2019-05-14 02:16:55 +02:00
|
|
|
|
2022-04-30 01:08:58 +02:00
|
|
|
// when client is nil allow only certain messages to be handled
|
|
|
|
if w.client == nil {
|
|
|
|
switch msg.(type) {
|
|
|
|
case *types.Connect, *types.Reconnect, *types.Disconnect, *types.Configure:
|
|
|
|
default:
|
|
|
|
return errClientNotReady
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-30 01:08:55 +02:00
|
|
|
// set connection timeout for calls to imap server
|
|
|
|
if w.client != nil {
|
|
|
|
w.client.Timeout = w.config.connection_timeout
|
|
|
|
}
|
2020-02-15 12:37:18 +01:00
|
|
|
|
2018-01-10 17:19:45 +01:00
|
|
|
switch msg := msg.(type) {
|
2018-02-02 01:54:19 +01:00
|
|
|
case *types.Unsupported:
|
2018-01-14 11:30:11 +01:00
|
|
|
// No-op
|
2018-02-02 01:54:19 +01:00
|
|
|
case *types.Configure:
|
2022-04-30 01:08:54 +02:00
|
|
|
reterr = w.handleConfigure(msg)
|
2018-02-02 01:54:19 +01:00
|
|
|
case *types.Connect:
|
2022-01-15 12:07:02 +01:00
|
|
|
if w.client != nil && w.client.State() == imap.SelectedState {
|
2022-04-30 01:08:56 +02:00
|
|
|
if !w.observer.AutoReconnect() {
|
|
|
|
w.observer.SetAutoReconnect(true)
|
|
|
|
w.observer.EmitIfNotConnected()
|
2022-02-05 00:07:16 +01:00
|
|
|
}
|
2022-04-30 01:08:54 +02:00
|
|
|
reterr = errAlreadyConnected
|
2022-01-19 13:18:08 +01:00
|
|
|
break
|
2021-11-01 21:38:26 +01:00
|
|
|
}
|
2022-02-05 00:07:16 +01:00
|
|
|
|
2022-04-30 01:08:56 +02:00
|
|
|
w.observer.SetAutoReconnect(true)
|
2021-12-06 23:52:25 +01:00
|
|
|
c, err := w.connect()
|
|
|
|
if err != nil {
|
2022-04-30 01:08:56 +02:00
|
|
|
w.observer.EmitIfNotConnected()
|
2022-01-19 13:18:08 +01:00
|
|
|
reterr = err
|
|
|
|
break
|
2018-01-14 11:30:11 +01:00
|
|
|
}
|
2022-01-15 12:07:02 +01:00
|
|
|
|
2022-04-30 01:08:55 +02:00
|
|
|
w.newClient(c)
|
2022-01-19 13:18:09 +01:00
|
|
|
|
2022-03-18 09:53:02 +01:00
|
|
|
w.worker.PostMessage(&types.Done{Message: types.RespondTo(msg)}, nil)
|
2022-01-19 13:18:10 +01:00
|
|
|
case *types.Reconnect:
|
2022-04-30 01:08:56 +02:00
|
|
|
if !w.observer.AutoReconnect() {
|
2022-01-19 13:18:10 +01:00
|
|
|
reterr = fmt.Errorf("auto-reconnect is disabled; run connect to enable it")
|
|
|
|
break
|
|
|
|
}
|
|
|
|
c, err := w.connect()
|
|
|
|
if err != nil {
|
2022-04-30 01:08:56 +02:00
|
|
|
errReconnect := w.observer.DelayedReconnect()
|
|
|
|
reterr = errors.Wrap(errReconnect, err.Error())
|
2022-01-19 13:18:10 +01:00
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2022-04-30 01:08:55 +02:00
|
|
|
w.newClient(c)
|
2022-01-19 13:18:10 +01:00
|
|
|
|
2022-03-18 09:53:02 +01:00
|
|
|
w.worker.PostMessage(&types.Done{Message: types.RespondTo(msg)}, nil)
|
2021-11-01 21:38:26 +01:00
|
|
|
case *types.Disconnect:
|
2022-04-30 01:08:56 +02:00
|
|
|
w.observer.SetAutoReconnect(false)
|
|
|
|
w.observer.Stop()
|
2021-11-22 21:31:42 +01:00
|
|
|
if w.client == nil || w.client.State() != imap.SelectedState {
|
2022-04-30 01:08:54 +02:00
|
|
|
reterr = errNotConnected
|
2022-01-19 13:18:08 +01:00
|
|
|
break
|
2021-11-01 21:38:26 +01:00
|
|
|
}
|
2022-01-19 13:18:09 +01:00
|
|
|
|
2021-11-01 21:38:26 +01:00
|
|
|
if err := w.client.Logout(); err != nil {
|
2022-01-19 13:18:08 +01:00
|
|
|
reterr = err
|
|
|
|
break
|
2021-11-01 21:38:26 +01:00
|
|
|
}
|
2022-03-18 09:53:02 +01:00
|
|
|
w.worker.PostMessage(&types.Done{Message: types.RespondTo(msg)}, nil)
|
2018-02-02 01:54:19 +01:00
|
|
|
case *types.ListDirectories:
|
2018-02-02 01:34:08 +01:00
|
|
|
w.handleListDirectories(msg)
|
2019-01-13 22:18:10 +01:00
|
|
|
case *types.OpenDirectory:
|
|
|
|
w.handleOpenDirectory(msg)
|
2019-03-11 04:45:00 +01:00
|
|
|
case *types.FetchDirectoryContents:
|
|
|
|
w.handleFetchDirectoryContents(msg)
|
2021-11-12 18:12:02 +01:00
|
|
|
case *types.FetchDirectoryThreaded:
|
|
|
|
w.handleDirectoryThreaded(msg)
|
2019-06-08 19:41:56 +02:00
|
|
|
case *types.CreateDirectory:
|
|
|
|
w.handleCreateDirectory(msg)
|
2020-08-18 22:27:23 +02:00
|
|
|
case *types.RemoveDirectory:
|
|
|
|
w.handleRemoveDirectory(msg)
|
2019-03-15 03:19:04 +01:00
|
|
|
case *types.FetchMessageHeaders:
|
|
|
|
w.handleFetchMessageHeaders(msg)
|
2019-03-31 18:14:37 +02:00
|
|
|
case *types.FetchMessageBodyPart:
|
|
|
|
w.handleFetchMessageBodyPart(msg)
|
2019-03-31 18:17:57 +02:00
|
|
|
case *types.FetchFullMessages:
|
|
|
|
w.handleFetchFullMessages(msg)
|
imap: add option to cache headers
Add option to cache headers for imap accounts. Cache db is located at
$XDG_CACHE_DIR/aerc/{account name}. The cache is cleaned of stale
entries when aerc is first opened.
Two new account level configuration options are introduced:
* cache-headers (Default: false)
* cache-max-age (Default: 30 days (720 hours))
The change in worker/imap/open.go is to set the selected directory. This
is required to access the UIDVALIDITY field, which is used in
combination with the message ID to form the key for use in the cache db.
The key structure is: "header.{UIDVALIDITY}.{UID}"
Where reasonable, cache does not stop aerc from running. In general, if
there is an error in the cache, aerc should continue working as usual.
Errors are either displayed to the user or logged.
All messages are stored without flags, and when retrieved have the flags
set to SEEN. This is to prevent UI flashes. A new method to
FetchMessageFlags is introduced to update flags of cached headers. This
is done asynchronously, and the user will see their messages appear and
then any flags updated. The message will initially show as SEEN, but
will update to unread. I considered updating the cache with the
last-known flag state, however it seems prudent to spare the R/W cycle
and assume that - eventually - all messages will end up read, and if it
isn't the update will occur rather quickly.
Note that leveldb puts a lock on the database, preventing multiple
instances of aerc from accessing the cache at the same time.
Much of this work is based on previous efforts by Vladimír Magyar.
Implements: https://todo.sr.ht/~rjarry/aerc/2
Thanks: Vladimír Magyar <vladimir@mgyar.me>
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
Tested-by: inwit <inwit@sindominio.net>
Reviewed-by: Koni Marti <koni.marti@gmail.com>
Acked-by: Robin Jarry <robin@jarry.cc>
2022-06-15 14:23:51 +02:00
|
|
|
case *types.FetchMessageFlags:
|
|
|
|
w.handleFetchMessageFlags(msg)
|
2019-03-21 04:23:38 +01:00
|
|
|
case *types.DeleteMessages:
|
|
|
|
w.handleDeleteMessages(msg)
|
2020-07-05 16:29:52 +02:00
|
|
|
case *types.FlagMessages:
|
|
|
|
w.handleFlagMessages(msg)
|
2020-05-25 16:59:48 +02:00
|
|
|
case *types.AnsweredMessages:
|
|
|
|
w.handleAnsweredMessages(msg)
|
2019-05-14 22:34:42 +02:00
|
|
|
case *types.CopyMessages:
|
|
|
|
w.handleCopyMessages(msg)
|
2022-08-16 23:23:39 +02:00
|
|
|
case *types.MoveMessages:
|
|
|
|
w.handleMoveMessages(msg)
|
2019-05-16 01:41:21 +02:00
|
|
|
case *types.AppendMessage:
|
|
|
|
w.handleAppendMessage(msg)
|
2019-06-24 22:31:37 +02:00
|
|
|
case *types.SearchDirectory:
|
|
|
|
w.handleSearchDirectory(msg)
|
2022-05-30 14:34:18 +02:00
|
|
|
case *types.CheckMail:
|
|
|
|
w.handleCheckMailMessage(msg)
|
2018-01-14 11:30:11 +01:00
|
|
|
default:
|
2020-02-15 12:37:18 +01:00
|
|
|
reterr = errUnsupported
|
2018-01-10 02:39:00 +01:00
|
|
|
}
|
2019-05-14 02:16:55 +02:00
|
|
|
|
2022-04-30 01:08:55 +02:00
|
|
|
// we don't want idle to timeout, so set timeout to zero
|
|
|
|
if w.client != nil {
|
|
|
|
w.client.Timeout = 0
|
|
|
|
}
|
|
|
|
|
2020-02-15 12:37:18 +01:00
|
|
|
return reterr
|
2018-01-10 02:39:00 +01:00
|
|
|
}
|
|
|
|
|
2019-01-13 22:18:10 +01:00
|
|
|
func (w *IMAPWorker) handleImapUpdate(update client.Update) {
|
2022-07-19 22:31:51 +02:00
|
|
|
logging.Debugf("(= %T", update)
|
2019-01-13 22:18:10 +01:00
|
|
|
switch update := update.(type) {
|
|
|
|
case *client.MailboxUpdate:
|
|
|
|
status := update.Mailbox
|
2019-03-11 04:45:00 +01:00
|
|
|
if w.selected.Name == status.Name {
|
2019-08-22 14:53:27 +02:00
|
|
|
w.selected = status
|
2019-03-11 04:45:00 +01:00
|
|
|
}
|
2019-01-13 22:18:10 +01:00
|
|
|
w.worker.PostMessage(&types.DirectoryInfo{
|
2019-07-08 04:43:56 +02:00
|
|
|
Info: &models.DirectoryInfo{
|
|
|
|
Flags: status.Flags,
|
|
|
|
Name: status.Name,
|
|
|
|
ReadOnly: status.ReadOnly,
|
|
|
|
|
|
|
|
Exists: int(status.Messages),
|
|
|
|
Recent: int(status.Recent),
|
|
|
|
Unseen: int(status.Unseen),
|
2022-07-05 21:48:37 +02:00
|
|
|
Caps: w.caps,
|
2019-07-08 04:43:56 +02:00
|
|
|
},
|
2019-01-13 22:18:10 +01:00
|
|
|
}, nil)
|
2019-06-09 20:55:04 +02:00
|
|
|
case *client.MessageUpdate:
|
|
|
|
msg := update.Message
|
|
|
|
if msg.Uid == 0 {
|
2022-06-20 21:49:05 +02:00
|
|
|
if uid, found := w.seqMap.Get(msg.SeqNum); !found {
|
2022-07-19 22:31:51 +02:00
|
|
|
logging.Errorf("MessageUpdate unknown seqnum: %d", msg.SeqNum)
|
2022-04-28 21:51:54 +02:00
|
|
|
return
|
2022-06-20 21:49:05 +02:00
|
|
|
} else {
|
|
|
|
msg.Uid = uid
|
2022-04-28 21:51:54 +02:00
|
|
|
}
|
2019-06-09 20:55:04 +02:00
|
|
|
}
|
2022-08-01 19:18:25 +02:00
|
|
|
if int(msg.SeqNum) > w.seqMap.Size() {
|
|
|
|
w.seqMap.Put(msg.Uid)
|
|
|
|
}
|
2019-06-09 20:55:04 +02:00
|
|
|
w.worker.PostMessage(&types.MessageInfo{
|
2019-07-08 04:43:56 +02:00
|
|
|
Info: &models.MessageInfo{
|
2019-07-08 04:43:58 +02:00
|
|
|
BodyStructure: translateBodyStructure(msg.BodyStructure),
|
|
|
|
Envelope: translateEnvelope(msg.Envelope),
|
2020-03-03 14:45:06 +01:00
|
|
|
Flags: translateImapFlags(msg.Flags),
|
2019-07-08 04:43:56 +02:00
|
|
|
InternalDate: msg.InternalDate,
|
|
|
|
Uid: msg.Uid,
|
|
|
|
},
|
2019-06-09 20:55:04 +02:00
|
|
|
}, nil)
|
2019-05-14 02:23:23 +02:00
|
|
|
case *client.ExpungeUpdate:
|
2022-06-20 21:49:05 +02:00
|
|
|
if uid, found := w.seqMap.Pop(update.SeqNum); !found {
|
2022-07-19 22:31:51 +02:00
|
|
|
logging.Errorf("ExpungeUpdate unknown seqnum: %d", update.SeqNum)
|
2022-06-20 21:49:05 +02:00
|
|
|
} else {
|
|
|
|
w.worker.PostMessage(&types.MessagesDeleted{
|
|
|
|
Uids: []uint32{uid},
|
|
|
|
}, nil)
|
2022-04-28 21:51:54 +02:00
|
|
|
}
|
2019-01-13 22:18:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-10 02:39:00 +01:00
|
|
|
func (w *IMAPWorker) Run() {
|
|
|
|
for {
|
|
|
|
select {
|
2018-02-02 00:42:03 +01:00
|
|
|
case msg := <-w.worker.Actions:
|
|
|
|
msg = w.worker.ProcessAction(msg)
|
2022-04-30 01:08:55 +02:00
|
|
|
|
2022-07-31 15:15:27 +02:00
|
|
|
if err := w.handleMessage(msg); errors.Is(err, errUnsupported) {
|
2018-02-02 01:54:19 +01:00
|
|
|
w.worker.PostMessage(&types.Unsupported{
|
2018-01-14 11:30:11 +01:00
|
|
|
Message: types.RespondTo(msg),
|
2018-02-02 00:42:03 +01:00
|
|
|
}, nil)
|
2018-01-14 11:30:11 +01:00
|
|
|
} else if err != nil {
|
2018-02-02 01:54:19 +01:00
|
|
|
w.worker.PostMessage(&types.Error{
|
2018-01-14 11:30:11 +01:00
|
|
|
Message: types.RespondTo(msg),
|
|
|
|
Error: err,
|
2018-02-02 00:42:03 +01:00
|
|
|
}, nil)
|
2018-01-14 11:30:11 +01:00
|
|
|
}
|
2022-04-30 01:08:55 +02:00
|
|
|
|
2018-01-14 11:30:11 +01:00
|
|
|
case update := <-w.updates:
|
2019-01-13 22:18:10 +01:00
|
|
|
w.handleImapUpdate(update)
|
2018-01-10 02:39:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|