fix(pop3): wrap underlying errors (#1320)

Matt Van Horn created

## What?

`findAttachmentData` in `backend/pop3/pop3.go` had three error sites
that dropped underlying context:

- `gomail.CreateReader` returned an error but the wrapping call
discarded it with a static `"not a multipart message"` string.
- The `mr.NextPart()` loop broke on a non-EOF error without saving it,
so the final return at the bottom of the function reported "not found"
instead of the real read failure.
- The not-found error had no count of how many parts the loop actually
scanned, which made the failure hard to debug from a log line alone.

This PR wraps the `CreateReader` error with `%w`, captures the
`NextPart` error in a local `scanErr` and surfaces it as the final error
when it is set, and includes the scanned-parts count in the not-found
message.

## Why?

Fixes #688. The reporter noted the missing `%w` wrapping at the original
line 438. While poking at that site I noticed the silent `break` on
`mr.NextPart()` errors right above it. Those two paths share the same
downstream "not found" return, so a multipart parse error in the middle
of the stream looks identical to a genuinely missing part. The third
change (scanned-parts count) is the smallest possible nudge that makes
the two failure modes distinguishable in an error string without
changing the return type or call sites.

Fixes #688

Change summary

backend/pop3/pop3.go | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)

Detailed changes

backend/pop3/pop3.go 🔗

@@ -465,16 +465,18 @@ func parseMessageBody(r io.Reader) (string, string, []backend.Attachment, error)
 func findAttachmentData(r io.Reader, targetPartID string) ([]byte, error) {
 	mr, err := gomail.CreateReader(r)
 	if err != nil {
-		return nil, fmt.Errorf("not a multipart message")
+		return nil, fmt.Errorf("pop3: not a multipart message: %w", err)
 	}
 
 	partIdx := 0
+	var scanErr error
 	for {
 		part, err := mr.NextPart()
 		if err == io.EOF {
 			break
 		}
 		if err != nil {
+			scanErr = err
 			break
 		}
 		partIdx++
@@ -484,5 +486,9 @@ func findAttachmentData(r io.Reader, targetPartID string) ([]byte, error) {
 		}
 	}
 
-	return nil, fmt.Errorf("pop3: attachment part %s not found", targetPartID)
+	if scanErr != nil {
+		return nil, fmt.Errorf("pop3: failed to scan attachment parts: %w", scanErr)
+	}
+
+	return nil, fmt.Errorf("pop3: attachment part %s not found (scanned %d parts)", targetPartID, partIdx)
 }