package imapx import ( "context" "time" "github.com/emersion/go-imap/v2/imapclient" ) // KeepaliveInterval is how often an otherwise-idle IMAP connection is pinged // with NOOP so the server does not drop it. // // The destination connection sits completely idle for the entire duration of // the source-side metadata scan (Pass 1 of CopyFolder), which on a large // mailbox runs for many minutes across all folders. With no traffic, the // server closes the idle connection; go-imap's reader then tears the client // down, and every subsequent APPEND fails with "use of closed network // connection" — aborting each folder and copying nothing. A periodic NOOP // keeps the connection warm. 60s is well under the idle timeout of any common // IMAP server. const KeepaliveInterval = 60 * time.Second // Keepalive pings c with a NOOP every interval until ctx is cancelled, keeping // an idle connection from being dropped by the server. It is meant to run in // its own goroutine. // // It is safe to run concurrently with other commands on c: go-imap serializes // command submission and supports multiple in-flight commands over a single // connection (RFC 9051 §5.5 pipelining). A NOOP issued while another command is // in flight simply queues behind it and completes when the server responds. // // Keepalive returns when ctx is done or when a NOOP fails — a failed NOOP means // the connection is already gone, so there is nothing left to keep alive. func Keepalive(ctx context.Context, c *imapclient.Client, interval time.Duration) { t := time.NewTicker(interval) defer t.Stop() for { select { case <-ctx.Done(): return case <-t.C: if err := c.Noop().Wait(); err != nil { return } } } }