0ffd4bdbea
Comment/XML-doc changes only, no code logic: - strip project-tracking references (WP*, P*-*, row-NN, #NN, Task N) from comments, keeping the explanatory rationale - add missing summaries, <param>, <returns>, <typeparam> and <inheritdoc/> - replace misused <inheritdoc/> with real docs; where <inheritdoc/> was combined with useful remarks, move the rationale into // comments - drop stale replicationActor <param> on DeploymentManagerActor ctor Remaining 5 are false positives (CWE-1236 x2, failures-1, year-0001, M365). Build 0 errors / 0 warnings.
95 lines
4.4 KiB
C#
95 lines
4.4 KiB
C#
using System.Text;
|
|
using System.Xml.Linq;
|
|
|
|
namespace ZB.MOM.WW.ScadaBridge.NotificationService.Ews;
|
|
|
|
/// <summary>
|
|
/// Builds the EWS <c>CreateItem</c> SOAP envelope used to send a notification email
|
|
/// through Exchange Web Services.
|
|
/// <para>
|
|
/// 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.
|
|
/// </para>
|
|
/// </summary>
|
|
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";
|
|
|
|
/// <summary>
|
|
/// Builds a <c>CreateItem</c> request that sends one plain-text message.
|
|
/// <para>
|
|
/// <c>MessageDisposition="SendOnly"</c> sends without keeping a Sent Items copy, and every
|
|
/// recipient is addressed via <c>BccRecipients</c> so recipients cannot see one another —
|
|
/// both mirror the SMTP delivery path's semantics.
|
|
/// </para>
|
|
/// </summary>
|
|
/// <param name="fromAddress">The sending mailbox address stamped into <c>t:From</c>.</param>
|
|
/// <param name="bccRecipients">
|
|
/// The recipient addresses; each becomes one <c>t:Mailbox</c> under <c>t:BccRecipients</c>.
|
|
/// Callers are expected to supply at least one — an empty list yields an envelope Exchange
|
|
/// rejects as schema-invalid.
|
|
/// </param>
|
|
/// <param name="subject">The message subject; escaped, not interpreted.</param>
|
|
/// <param name="body">The plain-text message body (<c>BodyType="Text"</c>); escaped, not interpreted.</param>
|
|
/// <returns>The complete SOAP envelope as XML text, prefixed with a UTF-8 declaration.</returns>
|
|
public static string BuildCreateItem(
|
|
string fromAddress,
|
|
IReadOnlyList<string> 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));
|
|
|
|
/// <summary>
|
|
/// A <see cref="StringWriter"/> that reports UTF-8 so <see cref="XDocument.Save(TextWriter)"/>
|
|
/// emits <c>encoding="utf-8"</c> instead of the default UTF-16.
|
|
/// </summary>
|
|
private sealed class Utf8StringWriter : StringWriter
|
|
{
|
|
/// <inheritdoc />
|
|
public override Encoding Encoding => Encoding.UTF8;
|
|
}
|
|
}
|