jonhoo / rust-imap

IMAP client library for Rust

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Unable to parse status response

soywod opened this issue · comments

Some of himalaya users reported me this error while trying to display their messages. The error occurs there:

let fetches = self
    .sess()?
    .fetch(&range, "(ENVELOPE FLAGS INTERNALDATE)")
    .context(format!(r#"cannot fetch messages within range "{}""#, range))?;

It looks like the response from the IMAP server cannot be parsed properly. The problem is that:

  1. I cannot reproduce their error (it looks like it fails in a specific context)
  2. I cannot see which response fails because it is not included in the final error message:
    ParseError::Invalid(_) => f.write_str("Unable to parse status response"),

Any idea?

Hmm, interesting. We should probably include the invalid content in the error. In the meantime I would offer your users a way to enable imap's debug mode like here:

imap.debug = true;

Basically, just set .debug = true on your Session. That should print the underlying traffic to stdout, which can be used to debug further!

Thanks for the tip, I will activate them and let you know if I have more information!

Someone was able to reproduce the issue with debug enabled, it looks like the mailbox cannot be found:

C: a2 SELECT "INBOX"
S: * FLAGS (\Answered \Flagged \Draft \Deleted \Seen $Forwarded $MailFlagBit0 $NotJunk $NotPhishing $Phishing $has_cal LoadRemoteImages Mail Networking NotJunk OIB-Seen- OIB-Seen-INBOX OIB-Seen-OIB/Business OIB-Seen-OIB/Entertainment OIB-Seen-OIB/Finance OIB-Seen-OIB/Groups OIB-Seen-OIB/Home OIB-Seen-OIB/Jobs OIB-Seen-OIB/Shopping OIB-Seen-OIB/Social OIB-Seen-OIB/Social Networking OIB-Seen-[Gmail]/All Mail OIB-Seen-[Gmail]/Important OIB-Seen-[Gmail]/Spam OIB-Seen-[Gmail]/Trash Old receipt-handled)
Error: cannot select mailbox INBOX

But when listing mailboxes we can see the INBOX one:

C: a2 LIST "" *
S: * LIST (\HasNoChildren) "/" "Archive"
S: * LIST (\HasNoChildren) "/" "INBOX"
S: * LIST (\HasChildren \Noselect) "/" "[Gmail]"
S: * LIST (\Drafts \HasNoChildren) "/" "[Gmail]/Drafts"
S: * LIST (\HasNoChildren \Important) "/" "[Gmail]/Important"
S: * LIST (\HasNoChildren \Sent) "/" "[Gmail]/Sent Mail"
S: * LIST (\HasNoChildren \Junk) "/" "[Gmail]/Spam"
S: * LIST (\Flagged \HasNoChildren) "/" "[Gmail]/Starred"
S: * LIST (\HasNoChildren \Trash) "/" "[Gmail]/Trash"
S: a2 OK Success

Any idea?

Hmm, that's interesting — do they not get an a2 OK in that first response? That makes it seem as though the server just sends the flags but doesn't confirm the operation. If there is an OK hiding there, then I wonder if we fail to parse the FLAGS line somehow. Could you try adding that whole line into a unit test and see that it parses without error (maybe even just directly parse it with imap_proto)?

Sorry for the delay! I added this test:

    #[test]
    fn parse_bad_mailbox() {
        let lines = b"* FLAGS (\\Answered \\Flagged \\Draft \\Deleted \\Seen $Forwarded $MailFlagBit0 $NotJunk $NotPhishing $Phishing $has_cal LoadRemoteImages Mail Networking NotJunk OIB-Seen- OIB-Seen-INBOX OIB-Seen-OIB/Business OIB-Seen-OIB/Entertainment OIB-Seen-OIB/Finance OIB-Seen-OIB/Groups OIB-Seen-OIB/Home OIB-Seen-OIB/Jobs OIB-Seen-OIB/Shopping OIB-Seen-OIB/Social OIB-Seen-OIB/Social Networking OIB-Seen-[Gmail]/All Mail OIB-Seen-[Gmail]/Important OIB-Seen-[Gmail]/Spam OIB-Seen-[Gmail]/Trash Old receipt-handled)\r\n";
        let (mut send, recv) = mpsc::channel();
        parse_mailbox(lines, &mut send).unwrap();
        assert!(recv.try_recv().is_err());
    }

And I got this error:

// thread 'parse::tests::parse_bad_mailbox' panicked at 'called `Result::unwrap()` on an `Err` value: Parse(Invalid([42, 32, 70, …]))', src/parse.rs:571:41

When I log the sub error I have this:

// err: Error(Error { input: [42, 32, 70, …], code: TakeWhile1 })

After narrowing the string, it looks like the issue comes from the brackets OIB-Seen-[Gmail] 🤔

According to the RFC:

   flag            = "\Answered" / "\Flagged" / "\Deleted" /
                     "\Seen" / "\Draft" / flag-keyword / flag-extension
                       ; Does not include "\Recent"

A flag-keyword is:

   flag-keyword    = "$MDNSent" / "$Forwarded" / "$Junk" /
                     "$NotJunk" / "$Phishing" / atom

And an atom is:

   atom            = 1*ATOM-CHAR

   ATOM-CHAR       = <any CHAR except atom-specials>

   atom-specials   = "(" / ")" / "{" / SP / CTL / list-wildcards /
                     quoted-specials / resp-specials

   resp-specials   = "]"

So the error is totally normal, a closing bracket is not a valid char for a custom Flag…

Knowing that, which strategy would you advise to deal with this kind of situation? This error seems to come often (see https://github.com/soywod/himalaya/issues/269). Would it make sense to add an option to be "less strict" on the parsing?

I'm curious what @djc (the maintainer of imap-proto) has to say here.

Seems to be a duplicate of djc/tokio-imap#36, see also things like https://bugs.python.org/issue21815 and jstedfast/MailKit#193. At this point probably make sense to just allow [ and ] in flags, I'd open to taking a patch for this in imap-proto. Would be really nice to figure out where this OIB-Seen crap comes from...

I have no idea how to write a clean solution 🙁 I will propose a PR so we can discuss on!

The PR has been merged, let me know when a new release is available 🙂

imap-proto 0.16.2 is on crates.io now.

@jonhoo when could you possibly release a new version? No pressure of course, it is just to know when I will be able to release mine 🙂

@soywod I'm not sure what you mean? Given that all that's needed here is the newer version of imap-proto, and they did a point release, you should be able to get the fix just by doing cargo update. No new version of imap should be necessary.

@jonhoo I need a new version because I am using rust-imap, not imap-proto directly.

I mean, if I do not mistake, rust-imap needs to update imap-proto version so the fix can be accessible, right?

Nope, that shouldn't be necessary since imap specifies imap-proto = "0.16.1", which is compatible with 0.16.2 (which contains the fix).