File: System\Net\Mail\DotAtomReader.cs
Web Access
Project: src\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.Diagnostics;
using System.Net.Mime;
using System.Text;
 
namespace System.Net.Mail
{
    //
    // RFC 2822 Section 3.2.4 - Atom, Dot-Atom
    //
    // A Dot-Atom is a string of ASCII characters separated by dots.  Dots would normally not be allowed at the start
    // or end, but we do allow dots at the end for compatibility with other mail clients.  We don't allow
    // multiple consecutive dots as specified in RFC 2822 section 3.4.1.
    //
    internal static class DotAtomReader
    {
        // Reads a Dot Atom in reverse.
        //
        // Preconditions:
        //  - Index must be within the bounds of the data string.
        //
        // Return value:
        // - The first index of a character not valid in a dot-atom.  It is then up to the caller to
        //   determine if the next character is a valid delimiter.
        //   e.g. "user.name@domain.com", starting at index 19 (m) returns 9 (@).
        //   e.g. "user.name@dom in.com", starting at index 19 (m) returns 13 (space).
        // - -1 if the dot-atom section terminated at the start of the data string.
        //   e.g. "user.name@domain.com", starting at index 8 (e) returns -1.
        //
        // Throws FormatException or returns false if:
        // - The initial character at data[index] is invalid in a dot-atom.
        //   e.g. "a@b.com", starting at index 1 (@).
        // - The leading character is a dot.
        //   e.g. "a@.b.com", starting at index 7 (m), throws because the leading char (index 2) is a dot.
        //
        internal static bool TryReadReverse(string data, int index, out int outIndex, bool throwExceptionIfFail)
        {
            Debug.Assert(0 <= index && index < data.Length, $"index was outside the bounds of the string: {index}");
 
            int startIndex = index;
 
            // Scan for the first invalid chars (including whitespace)
            for (; 0 <= index; index--)
            {
                if (Ascii.IsValid(data[index]) // Any ASCII allowed
                 && ((data[index] != MailBnfHelper.Dot && !MailBnfHelper.Atext[data[index]])
                 || (data[index] == MailBnfHelper.Dot && index > 0 && data[index - 1] == MailBnfHelper.Dot))) // Invalid char
                {
                    break;
                }
            }
 
            // Check for empty/invalid dot-atom
            if (startIndex == index)
            {
                if (throwExceptionIfFail)
                {
                    throw new FormatException(SR.Format(SR.MailHeaderFieldInvalidCharacter, data[index]));
                }
                else
                {
                    outIndex = default;
                    return false;
                }
            }
            else if (index > 0 && data[index] == MailBnfHelper.Dot && data[index - 1] == MailBnfHelper.Dot)
            {
                if (throwExceptionIfFail)
                {
                    throw new FormatException(SR.Format(SR.MailHeaderFieldInvalidCharacter, MailBnfHelper.ConsecutiveDots));
                }
                else
                {
                    outIndex = default;
                    return false;
                }
            }
            // Check for leading dot
            else if (data[index + 1] == MailBnfHelper.Dot)
            {
                if (throwExceptionIfFail)
                {
                    throw new FormatException(SR.Format(SR.MailHeaderFieldInvalidCharacter, MailBnfHelper.Dot));
                }
                else
                {
                    outIndex = default;
                    return false;
                }
            }
 
            outIndex = index;
            return true;
        }
    }
}