2

We were having a bit of a problem at a client when trying to open a mail folder and it couldn't be found so I created the below method to try my best to find the folder namespace that the mail folder was under.

private FolderNamespace FindFolderNamespace(ImapClient imapClient, string folderName)
{
    string[] folderNameParts = folderName.Split('/');

    FolderNamespaceCollection folderNamespaces = new FolderNamespaceCollection();
    foreach (FolderNamespace folderNamespace in imapClient.PersonalNamespaces) folderNamespaces.Add(folderNamespace);
    foreach (FolderNamespace folderNamespace in imapClient.SharedNamespaces) folderNamespaces.Add(folderNamespace);
    foreach (FolderNamespace folderNamespace in imapClient.OtherNamespaces) folderNamespaces.Add(folderNamespace);

    Dictionary<FolderNamespace, string> potentialFolderNamespaces = new Dictionary<FolderNamespace, string>();
    foreach (FolderNamespace folderNamespace in folderNamespaces)
    {
        IMailFolder mailFolder = imapClient.GetFolder(folderNamespace);
        foreach (string folderNamePart in folderNameParts)
        {
            if (mailFolder.GetSubfolders().Any(mf => mf.Name.Equals(folderNamePart, StringComparison.OrdinalIgnoreCase)))
            {
                mailFolder = mailFolder.GetSubfolder(folderNamePart);
            }
            else
            {
                break;
            }
        }

        if (mailFolder.FullName.IndexOf(folderName, StringComparison.OrdinalIgnoreCase) >= 0)
        {
            return folderNamespace;
        }

        if (!mailFolder.IsNamespace)
        {
            potentialFolderNamespaces.Add(folderNamespace, mailFolder.FullName);
        }
    }

    FolderNamespace closestFolderNameSpace = potentialFolderNamespaces.OrderByDescending(n => n.Value.Length).FirstOrDefault().Key;
    if (closestFolderNameSpace != null)
    {
        return closestFolderNameSpace;
    }

    FolderNamespace defaultFolderNamespace = folderNamespaces.FirstOrDefault();

    return defaultFolderNamespace;
}

However, this didn't work. On closer inspection it seems that PersonalNamespaces, SharedNamespaces and OtherNamespaces are all empty and so there are no folder namespaces for this account.

As a quick check I just tried the below:

imapClient.GetFolder(folderName)

However, this threw an ImapProtocolException saying:

The IMAP server has unexpectedly disconnected.

Trying with only the root folder name also did the same thing.

I'm at a bit of a loss how I can open a folder on this account as I can't seem to find it. How can I do it?


Some more details:

  • .Net 4.6.1
  • MailKit/MimeKit versions 1.1 but updating to 2.4.1 didn't help
  • Office365 IMAP account

Protocol log:

Connected to --cut--
S: * OK The Microsoft Exchange IMAP4 service is ready. --cut--
C: A00000000 CAPABILITY
S: * CAPABILITY IMAP4 IMAP4rev1 AUTH=PLAIN AUTH=XOAUTH2 SASL-IR UIDPLUS MOVE ID UNSELECT CHILDREN IDLE NAMESPACE LITERAL+
S: A00000000 OK CAPABILITY completed.
C: A00000001 AUTHENTICATE XOAUTH2 --cut--
S: A00000001 NO AUTHENTICATE failed.
C: A00000002 AUTHENTICATE PLAIN --cut--
S: A00000002 OK AUTHENTICATE completed.
C: A00000003 CAPABILITY
S: * CAPABILITY IMAP4 IMAP4rev1 AUTH=PLAIN AUTH=XOAUTH2 SASL-IR UIDPLUS MOVE ID UNSELECT CLIENTACCESSRULES CLIENTNETWORKPRESENCELOCATION BACKENDAUTHENTICATE CHILDREN IDLE NAMESPACE LITERAL+
S: A00000003 OK CAPABILITY completed.
C: A00000004 NAMESPACE
S: A00000004 BAD User is authenticated but not connected.
C: A00000005 LIST "" "INBOX"
S: A00000005 BAD User is authenticated but not connected.
S: * BYE Connection closed. 14
TheLethalCoder
  • 6,668
  • 6
  • 34
  • 69
  • Please get a protocol log because there should always be at least 1 namespace. https://github.com/jstedfast/MailKit/blob/master/FAQ.md#ProtocolLog – jstedfast Jan 09 '20 at 13:45
  • @jstedfast Done that, not really sure what I'm looking for in it though. – TheLethalCoder Jan 09 '20 at 13:50
  • That's why you've got to post it (after scrubbing the AUTHENTICATE or LOGIN command) so other people can analyze it – jstedfast Jan 09 '20 at 13:50
  • My guess is that the INBOX is probably the root namespace, but I won't know until I look at the protocol log (even so, the PersonalNamespaces should have 1 namespace). – jstedfast Jan 09 '20 at 13:53
  • @jstedfast Posted the protocol log. But yeah all 3 Namespaces are empty. – TheLethalCoder Jan 09 '20 at 13:56
  • The namespaces are empty because apparently the IMAP server is in some sort of broken state where it knows you are authenticated but thinks you aren't connected (which isn't even possible, because in order to be authenticated you *NEED* to be connected). – jstedfast Jan 09 '20 at 14:03
  • @jstedfast Would seeing if it is possible for our client to restart the IMAP server help at all... maybe? – TheLethalCoder Jan 09 '20 at 14:06
  • It's possible... I just noticed this is Exchange, and I know MailKit works with Exchange, so this is odd. Is the customer connecting to port 993? or port 143? Could they try reversing which port they connect to? – jstedfast Jan 09 '20 at 14:08
  • @jstedfast Port 993 with useSSL = true. – TheLethalCoder Jan 09 '20 at 14:09
  • Note: keep in mind that 993 is the SSL port, so the 3rd argument to Connect() will also need to change unless you use `Connect (hostName, port, SecureSocketOptions.Auto)` – jstedfast Jan 09 '20 at 14:09
  • I was hoping maybe the customer was on 143 and that *maybe* Exchange considered non-SSL connection mode to be "not connected". Still might be worth trying port 143, but that seems less likely to work. – jstedfast Jan 09 '20 at 14:11
  • Yeah didn't seem to work, I'll try a few combinations but it is odd. – TheLethalCoder Jan 09 '20 at 14:11
  • Aha! https://unix.stackexchange.com/questions/164823/user-is-authenticated-but-not-connected-after-changing-my-exchange-password – jstedfast Jan 09 '20 at 14:13
  • @jstedfast Oh incorrect password? Weird... I'll ask our client to check they've got the right one. – TheLethalCoder Jan 09 '20 at 14:15
  • FWIW, even if the password thing is the solution, I would still recommend keeping `client.AuthenticationMechanisms.Remove ("XOAUTH2");` until you upgrade to MailKit 2.x (2.x no longer attempts XOAUTH2 unless you explicitly tell it to). – jstedfast Jan 09 '20 at 14:17
  • @jstedfast Great, I'll keep it in and put it out in our next release! – TheLethalCoder Jan 09 '20 at 14:19

1 Answers1

2

Here's the problem:

C: A00000004 NAMESPACE
S: A00000004 BAD User is authenticated but not connected.
C: A00000005 LIST "" "INBOX"
S: A00000005 BAD User is authenticated but not connected.
S: * BYE Connection closed. 14

After authenticating, MailKit sends the NAMESPACE command to get the list of namespaces from the server, but it responds with a nonsensical error claiming that the client is authenticated but not connected (uh, that's impossible or we wouldn't be sending commands or receiving responses, duh).

When MailKit gets a BAD response for the NAMESPACE command, it falls back to trying to get information for the INBOX folder... for which it gets back the same BAD error which makes no sense.

Conclusion: The IMAP server is broken af.

Possible solution (other than getting a new IMAP server that doesn't suck):

This log looks like it was gotten using an old MailKit version (1.1?), so try doing this before calling Authenticate("username", "password"):

client.AuthenticationMechanisms.Remove ("XOAUTH2");

If that still results in the BAD NAMESPACE command, then I'm not sure what can be done...

Update:

Based on https://unix.stackexchange.com/questions/164823/user-is-authenticated-but-not-connected-after-changing-my-exchange-password - it sounds like Exchange IMAP has a bug where if the username is correct but the wrong password is given, the Exchange IMAP server will "authenticate" the user but get into this weird state of "authenticated but not connected" causing the above errors to occur.

The solution is to provide the correct password.

jstedfast
  • 35,744
  • 5
  • 97
  • 110