From 98b5a32230c3a41a1d4cda6e70807847a79a2d3b Mon Sep 17 00:00:00 2001 From: Sueun Cho Date: Wed, 15 Apr 2026 16:43:58 -0400 Subject: [PATCH] fix(imap): close debug log file handle to prevent fd leak (#525) Co-authored-by: drew --- fetcher/fetcher.go | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index 7f8d7fc9d1f248d0accc24e7495b491270e6de4c..75cc0ad4e60f168e8643da37f7056dabf58d5b1b 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -17,6 +17,7 @@ import ( "os" "slices" "strings" + "sync" "time" "github.com/ProtonMail/go-crypto/openpgp" @@ -30,6 +31,28 @@ import ( "golang.org/x/text/transform" ) +// debugIMAPFile holds a single shared file handle for IMAP debug logging, +// opened once via debugIMAPOnce to avoid leaking a descriptor per connection. +var ( + debugIMAPFile *os.File + debugIMAPOnce sync.Once +) + +func getDebugIMAPWriter() io.Writer { + debugIMAPOnce.Do(func() { + if path := os.Getenv("DEBUG_IMAP"); path != "" { + f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600) + if err == nil { + debugIMAPFile = f + } + } + }) + if debugIMAPFile != nil { + return debugIMAPFile + } + return nil +} + // Attachment holds data for an email attachment. type Attachment struct { Filename string @@ -243,10 +266,8 @@ func connectWithOptions(account *config.Account, extraOpts *imapclient.Options) options.UnilateralDataHandler = extraOpts.UnilateralDataHandler options.DebugWriter = extraOpts.DebugWriter } - if path := os.Getenv("DEBUG_IMAP"); path != "" { - if f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600); err == nil { - options.DebugWriter = f - } + if w := getDebugIMAPWriter(); w != nil { + options.DebugWriter = w } var c *imapclient.Client