I have a service running as a scheduled job on a machine. It's running under a service account that doesn't have it's own mailbox. We would like it to send emails from the team's shared inbox.
Using impersonation here is what I tried.
var service = new ExchangeService
{
TraceEnabled = true,
ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, Resources.EmailUsername)
};
service.AutodiscoverUrl(Resources.EmailUsername, RedirectionUrlValidationCallback);
var email = new EmailMessage(service);
if (!string.IsNullOrWhiteSpace(recipients))
{
foreach (var recipient in recipients.Split(','))
{
email.ToRecipients.Add(recipient.Trim());
}
}
email.Subject = subject;
email.Body = new MessageBody(BodyType.HTML, body);
if (attachmentName != null && attachment != null)
{
email.Attachments.AddFileAttachment(attachmentName, attachment);
}
email.Send();
It fails and I get an exception saying:
When making a request as an account that does not have a mailbox, you must specify the mailbox primary smtp address for any distinguished folder Ids.
The TraceEnabled
part led me to notice MessageDisposition=SaveOnly
was set in the xml. I looked up MessageDisposition and figured I wanted SendOnly.
After much searching I ended up here: https://github.com/OfficeDev/ews-managed-api/blob/master/Core/ServiceObjects/Items/EmailMessage.cs
Which shows:
public void Send()
{
this.InternalSend(null, MessageDisposition.SendOnly);
}
Well that looks like what I wanted in the first place... But then:
private void InternalSend(FolderId parentFolderId, MessageDisposition messageDisposition)
{
this.ThrowIfThisIsAttachment();
if (this.IsNew)
{
if ((this.Attachments.Count == 0) || (messageDisposition == MessageDisposition.SaveOnly))
{
this.InternalCreate(
parentFolderId,
messageDisposition,
null);
}
else
{
// If the message has attachments, save as a draft (and add attachments) before sending.
this.InternalCreate(
null, // null means use the Drafts folder in the mailbox of the authenticated user.
MessageDisposition.SaveOnly,
null);
this.Service.SendItem(this, parentFolderId);
}
}
...
The two comments are the most enlightening parts. The attachment is being saved to the drafts folder of the user running the process.
To get around this, the message must already be saved when we call Send. Let's make sure it is saved in the mailbox we know exists. So we remove the impersonation, add a step to save it, and modify the From field. Then we can safely send the message, and it will remove itself from the draft folder.
var service = new ExchangeService
{
TraceEnabled = true
};
service.AutodiscoverUrl(Resources.EmailUsername, RedirectionUrlValidationCallback);
var email = new EmailMessage(service);
if (!string.IsNullOrWhiteSpace(recipients))
{
foreach (var recipient in recipients.Split(','))
{
email.ToRecipients.Add(recipient.Trim());
}
}
email.Subject = subject;
email.Body = new MessageBody(BodyType.HTML, body);
if (attachmentName != null && attachment != null)
{
email.Attachments.AddFileAttachment(attachmentName, attachment);
}
var folderId = new FolderId(WellKnownFolderName.SentItems, Resources.EmailUsername);
email.Save(folderId);
email.From = Resources.EmailUsername;
email.Send();