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<name1#host1.tld>
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: name2#host2.tld
Content-Type: multipart/alternative;
Using the Redemption RDOMail.Recipients, I would expect that it would have one item and that it would be name2#host2.tld (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.
Updated with Solution
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:
Some Constants
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"
Test if the receiver is in the BCC
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;
}
Extract the current user from the BCC header
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: eugene#somedomain.com
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.
No, even BCC recipients won't be in the BCC header. You can generate the BCC header when saving in the MIME format, but it won't be present in messages received from a POP3 or IMAP4 server.
Generally, when a message is sent through SMTP, the server does not look at the MIME headers to figure out the recipients. The recipients are determined by the RCPT TO SMTP command. And no email client specifies BCC header in the outgoing messages - that would be a bug.
Related
I am sending emails via exchange web services (EWS C#), modern authentication and Exchange Online.
This works fine for all email addresses tested except one. This one email address is the main SMTP address of a user in our organization.
When I try to send the email it results in an error
"Microsoft.Exchange.WebServices.Data.ServiceResponseException: At least one recipient is not valid., Recipient 'xxxx.xxxx#yyy.com ' is not resolved. All recipients must be resolved before a message can be submitted."
The mail then resides in the draft folder of the sending mailbox.
If I view it there with Outlook then it shows 2-3 seconds the entered SMTP email address of the user and then it switches to this underlined resolved name view in Outlook.
Is there a way in EWS to trigger this resolve process manually? Or do I need a setting on the server?
Or am I missing something?
Thanks.
This is my code:
log.Debug("create email object");
EmailMessage message = new EmailMessage(service);
// Set properties on the email message.
message.Subject = subject;
message.Body = body;
message.From = from;
foreach (string emailReplyTo in replyTo)
{
message.ReplyTo.Add(emailReplyTo);
}
foreach (string emailTo in recipients)
{
message.ToRecipients.Add(emailTo);
}
if (cc != null)
{
foreach (string emailTo in cc)
{
message.CcRecipients.Add(emailTo);
}
}
if (bcc != null)
{
foreach (string emailTo in bcc)
{
message.BccRecipients.Add(emailTo);
}
}
if (attachmentData != null && !String.IsNullOrWhiteSpace(attachmentFileName))
{
log.Debug("Add attachment");
message.Attachments.AddFileAttachment(attachmentFileName, attachmentData);
}
log.Debug("Send and save copy of email");
message.SendAndSaveCopy();
log.Debug("Successfully sent email");
return true;
Funny thing. The issue was that the email address had a trailing blank.
The Exchange server is still able to resolve when viewing it in Outlook (it just takes some seconds) but in the code it creates the issue.
Our application sends out emails using the
new SmtpClient(smtpServer).Send(message);
We are making sure that the smtpServer is valid, the message has To and From addresses, a subject and a body. If any of these are missing, we would log an exception before even attempting to send a message.
But the application frequently fails with the below exception.
Email Fail: System.Net.Mail.SmtpException: Mailbox unavailable. The server response was: Too many invalid recipients.
Now, please note that the email send functionality is not failing always. For the same "To" address, it fails, may be about half the times. So, if the application tries sending out emails 100 times, we are getting some 40+ failures with the same message.
I have already validated that the To address and the From address exists. We are seeing this issue since last month when we shifted from Outlook to Gmail.
Here is the code.
if (fromAddress.Length == 0)
fromAddress = Resources.FromAddress;
if (toAddress.Length == 0) return "To Address is Required.";
if (smtpServer.Length == 0)
smtpServer = Resources.SMTPServer;
if (string.IsNullOrEmpty(smtpServer))
return "SMTP sever not specified";
MailMessage mailMessage = new MailMessage();
//set the addresses
mailMessage.From = new MailAddress(fromAddress);
string[] toAdds = toAddress.Split(';');
short i = 0;
foreach (string address in toAdds)
{
if(i==0) mailMessage.To.Add(address); else mailMessage.CC.Add(address);
i++;
}
if (!string.IsNullOrEmpty(bcc))
{
string[] bccAddresses = bcc.Split(';');
foreach (string address in bccAddresses)
{
mailMessage.Bcc.Add(address);
}
}
if (!string.IsNullOrEmpty(cc))
{
string[] ccAddresses = cc.Split(';');
foreach (string address in ccAddresses)
{
mailMessage.CC.Add(address);
}
}
if (subject.Length > 0)
mailMessage.Subject = subject;
mailMessage.Body = sBody;
mailMessage.IsBodyHtml = true;
SmtpClient emailClient = new SmtpClient(smtpServer);
emailClient.Send(mailMessage);
Any directions?
If the same email with the same sender and recipients is sometimes accepted, sometimes rejected by the SMTP server, it may be the result of a server antispam policy. For example :
Directory Harvest Attack Prevention (DHAP) : which causes a "550 Too many invalid recipients" error when exceeding a number of RCPT TO commands over a given period of time.
Quotas : a limit on the number of mails that a mailbox/IP can send per minute/second to prevent spamming.
You can validate if the SMTP server settings are the cause of your problem by (temporarily) :
whitelisting the IP address of your SMTP client
disabling any quota/antispam Policy applied to your sender's mailbox
If that doesn't solve your problem, then use a tool like WireShark to record the SMTP dialog and check exactly what email addresses are sent in the RCPT TO command, and in which cases the SMTP Server rejects them. Then, post it here.
I'm trying to obtain some information from emails sent to an Outlook email. I've successfully connected to the Exchange Server and have been able to retrieve some information from emails with attachments (I am skipping emails without attachments).
What I Have: I can retrieve the attachment file name, the email date, and the email subject.
What I Need: I need to retrieve the sender name and email also. From what I've done, I can retreive the body of the email (in HTML), but not the body text only (requires Exchange 2013 - Hello MS advertising).
I'm new to C# and today is my first time to connect to the Exchange Server. I noticed from reading around that "find" is limited in what it can obtain, and that I'll need to bind the message in order to get more information from the email.
Code thus far:
foreach (Item item in findResults.Items)
if (item.HasAttachments) // && item.Attachments[0] is FileAttachment)
{
item.Load();
FileAttachment fileAttachment = item.Attachments[0] as FileAttachment;
date = Convert.ToString(item.DateTimeCreated);
name = Convert.ToString(fileAttachment.Name);
fileAttachment.Load("C:\\test\\" + fileAttachment.Name);
Console.WriteLine(name);
Console.WriteLine(item.Subject);
Console.WriteLine(date);
}
My question from here is if I do EmailMessage msg = EmailMessage.Bind ... what information will I need in order to grab more information?
Solved - for getting sender email and name as well as loading an attachment.
I used the EmailMessage class (just added it in the above loop, and added the variables to the beginning):
EmailMessage msg = (EmailMessage)item;
senderemail = Convert.ToString(msg.Sender.Address);
sendername = Convert.ToString(msg.Sender.Name);
I can then reproduce these on the console:
Console.WriteLine(senderemail);
Console.WriteLine(sendername);
Also, for loading an email's attaachment, I declared a byte[] variable at the beginning, loaded the attachment, converted it, and wrote its content to the console:
fileAttachment.Load();
filecontent = fileAttachment.Content;
System.Text.Encoding enc = System.Text.Encoding.ASCII;
string strFileContent = enc.GetString(filecontent);
Console.WriteLine(strFileContent);
I get this error when I try to send an e-mail to a specific address in my code:
System.Net.Mail.SmtpFailedRecipientException: Mailbox unavailable. The server response was: Unknown user
The code sends an e-mail to two email addresses, mine and my colleague's. The e-mail sends to me just fine, but I get that error when it tries to send the email to him.
I looked around, and basically the common explanation for this error is that the email address is invalid, or their mailbox is full and isn't allowed to receive mail, or there is some setting on the server that is restricting it from receiving an e-mail.
But the email address is able to receive email, I'm corresponding back and forth through e-mail with him right now.
Is there any other reason why this error might occur?
EDIT:
Here's the code, maybe someone can spot an issue. I checked the parameters being passed, all the data is correct:
private static void SendEmail(IEnumerable<MailAddress> to, MailAddress from,
string subject, string body, string bodyHtml)
{
var mail = new MailMessage { From = from, Subject = subject };
foreach (var address in to)
{
mail.To.Add(address);
}
mail.AlternateViews.Add(
AlternateView.CreateAlternateViewFromString(bodyHtml, null, "text/html"));
mail.AlternateViews.Add(
AlternateView.CreateAlternateViewFromString(body, null, "text/plain"));
try
{
var smtp = new SmtpClient("localhost", 25)
{
Credentials = new NetworkCredential("xxx", "xxx")
};
smtp.Send(mail);
}
catch (Exception err)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(err);
}
}
Assuming your SMTP settings are correct this is most probably a case of a server-side restriction...
For example to prevent spam the server only accepts smtp from static sender IP and/or is checking sender IP against MX records (DNS) etc.
I'm looping sent item in EWS, and try to show details of each sent emails, receiver, subject, body etc. However, I found receiver in the sent email message is null. How to get receiver email address?
My code:
ItemId id = (ItemId)Request["id"]; // this id is the item id of WellKnownFolderName.**SentItems**
EmailMessage current = EmailMessage.Bind(service, id);
La_Subject.Text = current.Subject;
La_From.Text = current.Sender.ToString();
La_Sent.Text = current.DateTimeReceived.ToString();
La_To.Text = current.ReceivedBy.ToString(); // This line error occurs
Any idea?
To get the recipients of a mail, use the DisplayTo and DisplayCC properties of the mail message.
Or iterate through the ToRecipients collection yourself and build the string yourself:
var toRecipients = string.Join(", ",
mail.ToRecipients.Select(
address => string.Format("\"{0}\" <{1}", address.Name, address.Address)));
The ReceivedBy property is used in delegate scenarios. See http://msdn.microsoft.com/en-us/library/microsoft.exchange.webservices.data.emailmessage.receivedby(v=exchg.80).aspx.