emersion / go-imap

📥 An IMAP library for clients and servers

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

v2: FETCH can produce empty/imcomplete message (imapclient.FetchMessageBuffer)

Vovan-VE opened this issue · comments

Hello!

Giving the wollowing line from responce to FETCH:

* 85648 FETCH (UID 369004 BODY ((("text" "plain" ("charset" "koi8-r") NIL NIL "quoted-printable" 5716 0)("text" "html" ("charset" "koi8-r") NIL NIL "quoted-printable" 6181 0) "alternative")("message" "delivery-status" () NIL NIL "7bit" 340)("message" "rfc822" () NIL NIL "7bit" 23925) "report") ENVELOPE ("Thu, 02 Nov 2023 13:28:10 +0000" "=?utf-8?B?...?=" (("" NIL "foo" "example.org")) NIL NIL (("" NIL "bar" "example.net")) NIL NIL "<111.222@f124.i.mail.ru>" "<baz@example.com>") BODY[] {40909}

the imapclient.FetchCommand refuses to parse this line in the middle, silently producing imcomplete imapclient.FetchMessageBuffer with a data, fulfilled before the error occured.

And the error is returned by cmd.Close() is:

FETCH command Close error: in response-data: in body-type-mpart: in body-type-1part: imapwire: expected SP, got ")" / IMAP imap.mail.ru

Not sure exactly, but I suppose the error caused by empty list () in:

...("message" "delivery-status" () NIL NIL "7bit" 340)...

This case happens with different mail vendors.

v2.0.0-alpha.7

cmd := client.UIDFetch(wantedUids, &imap.FetchOptions{
	UID:           true,
	Envelope:      true,
	BodyStructure: &imap.FetchItemBodyStructure{},
	BodySection: []*imap.FetchItemBodySection{{
		Peek: true,
	}},
})
defer func() {
	if err := cmd.Close(); err != nil {
		log.Printf("FETCH command Close error: %v", err)
	}
}()
for msg := cmd.Next(); msg != nil; msg = cmd.Next() {
	buf, err := msg.Collect()
	if err != nil {
		return errors.Wrap(err, "msg.Collect()")
	}
	//...
}

BODY re-formatted for readability:

(
  (
    ("text" "plain" ("charset" "koi8-r") NIL NIL "quoted-printable" 5716 0)
    ("text" "html" ("charset" "koi8-r") NIL NIL "quoted-printable" 6181 0)
    "alternative"
  )
  ("message" "delivery-status" () NIL NIL "7bit" 340)
  ("message" "rfc822" () NIL NIL "7bit" 23925)
  "report"
)

I think the issue is that the message/rfc822 part does not specify the required extra parameters, stops at part size. However the RFC requires the extra parameters (see rule body-type-msg).

Can you try #559?

It works!

{Children: [{Children: [{Type:          "text",
                         Subtype:       "plain",
                         Params:        {charset: "koi8-r"},
                         ID:            "",
                         Description:   "",
                         Encoding:      "quoted-printable",
                         Size:          5716,
                         MessageRFC822: nil,
                         Text:          {NumLines: 0},
                         Extended:      nil},
                        {Type:          "text",
                         Subtype:       "html",
                         Params:        {charset: "koi8-r"},
                         ID:            "",
                         Description:   "",
                         Encoding:      "quoted-printable",
                         Size:          6181,
                         MessageRFC822: nil,
                         Text:          {NumLines: 0},
                         Extended:      nil}],
             Subtype:  "alternative",
             Extended: nil},
            {Type:          "message",
             Subtype:       "delivery-status",
             Params:        {},
             ID:            "",
             Description:   "",
             Encoding:      "7bit",
             Size:          340,
             MessageRFC822: nil,
             Text:          nil,
             Extended:      nil},
            {Type:          "message",
             Subtype:       "rfc822",
             Params:        {},
             ID:            "",
             Description:   "",
             Encoding:      "7bit",
             Size:          23925,
             MessageRFC822: nil,
             Text:          nil,
             Extended:      nil}],
 Subtype:  "report",
 Extended: nil}

Doing some interop testing... :-)

I believe @Vovan-VE had the right idea.

When I paste ...

* 1 FETCH (BODY ((("text" "plain" ("charset" "koi8-r") NIL NIL "quoted-printable" 5716 0)("text" "html" ("charset" "koi8-r") NIL NIL "quoted-printable" 6181 0) "alternative")("message" "delivery-status" () NIL NIL "7bit" 340)("message" "rfc822" () NIL NIL "7bit" 23925) "report"))

... into imap-codec, it errors out ...

But when I replace () with NIL ...

* 1 FETCH (BODY ((("text" "plain" ("charset" "koi8-r") NIL NIL "quoted-printable" 5716 0)("text" "html" ("charset" "koi8-r") NIL NIL "quoted-printable" 6181 0) "alternative")("message" "delivery-status" NIL NIL NIL "7bit" 340)("message" "rfc822" NIL NIL NIL "7bit" 23925) "report"))

... it works.

I believe this is a defect in mail.ru.

According to IMAP4rev1, we have ...

body-fld-param = "("
                   string SP string
                   *(SP string SP string)
                 ")" / nil

... and () is not valid.

It's interesting to note, that these "empty list" vs "no list (nil)" cases come up frequently. See modern-email/defects#12 (comment).

Let's collect all these things! :D