I'm working on an issue in our code base where we want to extract all recipients of an email message received by Outlook. I understand that if recipients are on the BCC line, the receivers of that email don't know about those recipients, but if you're on the BCC line, you do know (it's in the email headers). So I have a message that looks like this:
From: FirstName LastName<[email protected]>
Date: Thu, 16 Jul 2020 09:48:10 +0100
Message-ID: <CAE+EE4GS7RtDKgPGOUbTFQ3=7i9+QiKB++1cx7qqLd_09PRZFg@mail.gmail.com>
Subject: Testing
To: undisclosed-recipients:;
Bcc: [email protected]
Content-Type: multipart/alternative;
Using the Redemption RDOMail.Recipients, I would expect that it would have one item and that it would be [email protected]
(since that's all the information I have about recipients of this email). When I actually loop through the recipients, I get a count of 0. Shouldn't it return any BCC addresses it does know about?
Example Code (might be some syntax errors as I'm simplyfing live code):
public IEnumerable<string> GetRecipients(RDOMail mailItem)
{
var recipients = mailItem.Recipients;
for (var i = 0; i < recipients.Count; i++) // never enters as Count == 0
{
var recipient = recipients[i];
var smtpAddress = GetSmtpAddress(recipient);
yield return smtpAddress;
}
}
There is a BCC property of RDOMail, but it is also blank for received mail. The documentation does note: This property contains the display names only. The Recipients collection should be used to modify the BCC recipients.
Which leads me to believe that I can use the Recipients collection for Read operations as well.
Yes, I realize I might be able to assume that if the current address isn't on the TO or CC lines, it's on the BCC line. But this doesn't work for shared mailboxes... or rather, I'd have to do a lot of digging to understand where the received mail item is coming from in order to make the correct assumption.
I ended up working with Eugene's answer and combined it with looking at the SMTP BCC header for received email as well. There are still some cases (internal email where you're on the BCC header might not contain SMTP headers at all in an On-Prem Exchange Environment), but at this point, I think it's pretty complete. So I still use the above code for GetRecipients(RDOMail mailItem)
, but after I do that, I also call:
public const string PR_TRANSPORT_MESSAGE_HEADERS = "http://schemas.microsoft.com/mapi/proptag/0x007D001F"
public const string PID_TAG_RECEIVED_REPRESENTING_SMTP_ADDRESS = "http://schemas.microsoft.com/mapi/proptag/0x5D08001F"
private bool IsReceiverInBcc(RDOMail mailItem)
{
if (mailItem == null) return false;
var receiver = ConvertReceiverToContact(mailItem);
var messageHeaders = mailItem.Fields(Consts(PR_TRANSPORT_MESSAGE_HEADERS)?.ToString();
if (!string.IsNullOrWhiteSpace(messageHeaders)
{
var bccHeaderRegex = new Regex($"{Environment.NewLine}Bcc: .*{Environment.NewLine}", RegexOptions.IgnoreCase);
foreach (Match headerMatch in bccHeaderRegex.Matches(messageHeaders))
{
if (headerMatch?.Value?.IndexOf(receiver.address, CompareOptions.IgnoreCase) >= 0
{
return true;
}
}
}
return false;
}
private EmailContact ConvertReceiverToContact(RDOMail mailItem)
{
var contact = new EmailContact
{
Address = mailItem.Fields(Consts.PID_TAG_RECEIVED_REPRESENTING_SMTP_ADDRESS),
Name = mailItem.ReceivedByName
};
return contact;
}
The Outlook object model (as well as Extended MAPI) doesn't provide any information in the Recipients
collection. The Count
property returns 0 in this scenario. In the case of Extended MAPI the IMessage::GetRecipientTable method returns an empty table. Here is what MSDN states:
The
IMessage::GetRecipientTable
method returns a pointer to the message's recipient table, which includes information about all of the recipients for the message. There is one row for every recipient.
So, there are no issues in the Redemption library.
Based on my research performed with a sample email message recieved with the following content in the internet header:
To: undisclosed-recipients:;
Bcc: [email protected]
In the case of OOM, you can use the MailItem.ReceivedByName property which returns a string representing the display name of the true recipient for the mail message.
In the case of Extended MAPI (Redemption is a wrapper around this API), you can use the PidTagReceivedRepresentingSmtpAddress property which contains the SMTP email address of the user represented by the receiving mailbox owner. The DASL property name is http://schemas.microsoft.com/mapi/proptag/0x5D08001F
.