2
votes

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
1
Please get a protocol log because there should always be at least 1 namespace. github.com/jstedfast/MailKit/blob/master/FAQ.md#ProtocolLogjstedfast
@jstedfast Done that, not really sure what I'm looking for in it though.TheLethalCoder
That's why you've got to post it (after scrubbing the AUTHENTICATE or LOGIN command) so other people can analyze itjstedfast
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
@jstedfast Posted the protocol log. But yeah all 3 Namespaces are empty.TheLethalCoder

1 Answers

2
votes

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.