| File: System\Net\Mime\BaseWriter.cs | Web Access |
| Project: src\runtime\src\libraries\System.Net.Mail\src\System.Net.Mail.csproj (System.Net.Mail) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Specialized; using System.IO; using System.Net.Mail; using System.Runtime.ExceptionServices; namespace System.Net.Mime { internal abstract class BaseWriter { // This is the maximum default line length that can actually be written. When encoding // headers, the line length is more conservative to account for things like folding. // In MailWriter, all encoding has already been done so this will only fold lines // that are NOT encoded already, which means being less conservative is ok. private const int DefaultLineLength = 76; protected readonly BufferBuilder _bufferBuilder; protected readonly Stream _stream; private readonly EventHandler _onCloseHandler; private readonly bool _shouldEncodeLeadingDots; private readonly int _lineLength; protected Stream _contentStream = null!; // set to null on dispose protected bool _isInContent; protected BaseWriter(Stream stream, bool shouldEncodeLeadingDots) { ArgumentNullException.ThrowIfNull(stream); _stream = stream; _shouldEncodeLeadingDots = shouldEncodeLeadingDots; _onCloseHandler = new EventHandler(OnClose); _bufferBuilder = new BufferBuilder(); _lineLength = DefaultLineLength; } #region Headers internal abstract void WriteHeaders(NameValueCollection headers, bool allowUnicode); internal void WriteHeader(string name, string value, bool allowUnicode) { ArgumentNullException.ThrowIfNull(name); ArgumentNullException.ThrowIfNull(value); if (_isInContent) { throw new InvalidOperationException(SR.MailWriterIsInContent); } CheckBoundary(); _bufferBuilder.Append(name); _bufferBuilder.Append(": "); WriteAndFold(value, name.Length + 2, allowUnicode); _bufferBuilder.Append("\r\n"u8); } private void WriteAndFold(string value, int charsAlreadyOnLine, bool allowUnicode) { int lastSpace = 0, startOfLine = 0; for (int index = 0; index < value.Length; index++) { // When we find a FWS (CRLF) copy it as is. if (MailBnfHelper.IsFWSAt(value, index)) // At the first char of "\r\n " or "\r\n\t" { index += 2; // Skip the FWS _bufferBuilder.Append(value, startOfLine, index - startOfLine, allowUnicode); // Reset for the next line startOfLine = index; lastSpace = index; charsAlreadyOnLine = 0; } // When we pass the line length limit, and know where there was a space to fold at, fold there else if (((index - startOfLine) > (_lineLength - charsAlreadyOnLine)) && lastSpace != startOfLine) { _bufferBuilder.Append(value, startOfLine, lastSpace - startOfLine, allowUnicode); _bufferBuilder.Append("\r\n"u8); startOfLine = lastSpace; charsAlreadyOnLine = 0; } // Mark a foldable space. If we go over the line length limit, fold here. else if (value[index] == MailBnfHelper.Space || value[index] == MailBnfHelper.Tab) { lastSpace = index; } } // Write any remaining data to the buffer. if (value.Length - startOfLine > 0) { _bufferBuilder.Append(value, startOfLine, value.Length - startOfLine, allowUnicode); } } #endregion Headers #region Content internal Stream GetContentStream() { if (_isInContent) { throw new InvalidOperationException(SR.MailWriterIsInContent); } _isInContent = true; CheckBoundary(); _bufferBuilder.Append("\r\n"u8); Flush(); ClosableStream cs = new ClosableStream(new EightBitStream(_stream, _shouldEncodeLeadingDots), _onCloseHandler); _contentStream = cs; return cs; } #endregion Content #region Cleanup protected void Flush() { if (_bufferBuilder.Length > 0) { _stream.Write(_bufferBuilder.GetBuffer(), 0, _bufferBuilder.Length); _bufferBuilder.Reset(); } } internal abstract void Close(); protected abstract void OnClose(object? sender, EventArgs args); #endregion Cleanup protected virtual void CheckBoundary() { } } }