using System.Text;
using System.Xml.Linq;
namespace ZB.MOM.WW.ScadaBridge.NotificationService.Ews;
///
/// Builds the EWS CreateItem SOAP envelope used to send a notification email
/// through Exchange Web Services.
///
/// Pure and I/O-free: the HTTP send lives in the sender that consumes this. The envelope is
/// composed with LINQ-to-XML rather than string concatenation so operator-authored subject and
/// body content is XML-escaped by construction.
///
///
public static class EwsSoapEnvelope
{
private static readonly XNamespace Soap = "http://schemas.xmlsoap.org/soap/envelope/";
private static readonly XNamespace Types = "http://schemas.microsoft.com/exchange/services/2006/types";
private static readonly XNamespace Messages = "http://schemas.microsoft.com/exchange/services/2006/messages";
///
/// Builds a CreateItem request that sends one plain-text message.
///
/// MessageDisposition="SendOnly" sends without keeping a Sent Items copy, and every
/// recipient is addressed via BccRecipients so recipients cannot see one another —
/// both mirror the SMTP delivery path's semantics.
///
///
/// The sending mailbox address stamped into t:From.
///
/// The recipient addresses; each becomes one t:Mailbox under t:BccRecipients.
/// Callers are expected to supply at least one — an empty list yields an envelope Exchange
/// rejects as schema-invalid.
///
/// The message subject; escaped, not interpreted.
/// The plain-text message body (BodyType="Text"); escaped, not interpreted.
/// The complete SOAP envelope as XML text, prefixed with a UTF-8 declaration.
public static string BuildCreateItem(
string fromAddress,
IReadOnlyList bccRecipients,
string subject,
string body)
{
ArgumentNullException.ThrowIfNull(fromAddress);
ArgumentNullException.ThrowIfNull(bccRecipients);
ArgumentNullException.ThrowIfNull(subject);
ArgumentNullException.ThrowIfNull(body);
// EWS MessageType is an xs:sequence: Subject, Body, BccRecipients, From must appear in
// this order or the server answers ErrorSchemaValidation.
var message = new XElement(
Types + "Message",
new XElement(Types + "Subject", subject),
new XElement(Types + "Body", new XAttribute("BodyType", "Text"), body),
new XElement(Types + "BccRecipients", bccRecipients.Select(Mailbox)),
new XElement(Types + "From", Mailbox(fromAddress)));
var document = new XDocument(
new XDeclaration("1.0", "utf-8", null),
new XElement(
Soap + "Envelope",
new XAttribute(XNamespace.Xmlns + "soap", Soap.NamespaceName),
new XAttribute(XNamespace.Xmlns + "t", Types.NamespaceName),
new XAttribute(XNamespace.Xmlns + "m", Messages.NamespaceName),
new XElement(
Soap + "Header",
new XElement(Types + "RequestServerVersion", new XAttribute("Version", "Exchange2013"))),
new XElement(
Soap + "Body",
new XElement(
Messages + "CreateItem",
new XAttribute("MessageDisposition", "SendOnly"),
new XElement(Messages + "Items", message)))));
// XDocument.ToString() drops the declaration; saving through a writer that reports UTF-8
// keeps it, and keeps the declared encoding honest.
using var writer = new Utf8StringWriter();
document.Save(writer);
return writer.ToString();
}
private static XElement Mailbox(string address)
=> new(Types + "Mailbox", new XElement(Types + "EmailAddress", address));
///
/// A that reports UTF-8 so
/// emits encoding="utf-8" instead of the default UTF-16.
///
private sealed class Utf8StringWriter : StringWriter
{
///
public override Encoding Encoding => Encoding.UTF8;
}
}