/// <summary> /// Enumerates the message-id references such as those that can be found in /// the In-Reply-To or References header. /// </summary> /// <remarks> /// Incrementally parses Message-Ids (such as those from a References header /// in a MIME message) from the supplied buffer starting at the given index /// and spanning across the specified number of bytes. /// </remarks> /// <returns>The references.</returns> /// <param name="buffer">The raw byte buffer to parse.</param> /// <param name="startIndex">The index into the buffer to start parsing.</param> /// <param name="length">The length of the buffer to parse.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="buffer"/> is <c>null</c>. /// </exception> /// <exception cref="System.ArgumentOutOfRangeException"> /// <paramref name="startIndex"/> and <paramref name="length"/> do not specify /// a valid range in the byte array. /// </exception> public static IEnumerable <string> EnumerateReferences(byte[] buffer, int startIndex, int length) { ParseUtils.ValidateArguments(buffer, startIndex, length); int endIndex = startIndex + length; int index = startIndex; do { if (!ParseUtils.SkipCommentsAndWhiteSpace(buffer, ref index, endIndex, false)) { break; } if (index >= endIndex) { break; } if (buffer[index] == '<') { if (ParseUtils.TryParseMsgId(buffer, ref index, endIndex, true, false, out string msgid)) { yield return(msgid); } } else if (!ParseUtils.SkipWord(buffer, ref index, endIndex, false)) { index++; } } while (index < endIndex); yield break; }
/// <summary> /// Tries to parse a version from a header such as Mime-Version. /// </summary> /// <remarks> /// Parses a MIME version string from the supplied buffer starting at the given index /// and spanning across the specified number of bytes. /// </remarks> /// <returns><c>true</c>, if the version was successfully parsed, <c>false</c> otherwise.</returns> /// <param name="buffer">The raw byte buffer to parse.</param> /// <param name="startIndex">The index into the buffer to start parsing.</param> /// <param name="length">The length of the buffer to parse.</param> /// <param name="version">The parsed version.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="buffer"/> is <c>null</c>. /// </exception> /// <exception cref="System.ArgumentOutOfRangeException"> /// <paramref name="startIndex"/> and <paramref name="length"/> do not specify /// a valid range in the byte array. /// </exception> public static bool TryParse(byte[] buffer, int startIndex, int length, out Version version) { ParseUtils.ValidateArguments(buffer, startIndex, length); var values = new List <int> (); int endIndex = startIndex + length; int index = startIndex; int value; version = null; do { if (!ParseUtils.SkipCommentsAndWhiteSpace(buffer, ref index, endIndex, false) || index >= endIndex) { return(false); } if (!ParseUtils.TryParseInt32(buffer, ref index, endIndex, out value)) { return(false); } values.Add(value); if (!ParseUtils.SkipCommentsAndWhiteSpace(buffer, ref index, endIndex, false)) { return(false); } if (index >= endIndex) { break; } if (buffer[index++] != (byte)'.') { return(false); } } while (index < endIndex); switch (values.Count) { case 4: version = new Version(values[0], values[1], values[2], values[3]); break; case 3: version = new Version(values[0], values[1], values[2]); break; case 2: version = new Version(values[0], values[1]); break; default: return(false); } return(true); }
/// <summary> /// Enumerates the message-id references such as those that can be found in the In-Reply-To or References header. /// </summary> /// <returns>The references.</returns> /// <param name="buffer">The raw byte buffer to parse.</param> /// <param name="startIndex">The index into the buffer to start parsing.</param> /// <param name="length">The length of the buffer to parse.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="buffer"/> is <c>null</c>. /// </exception> /// <exception cref="System.ArgumentOutOfRangeException"> /// <paramref name="startIndex"/> and <paramref name="length"/> do not specify /// a valid range in the byte array. /// </exception> public static IEnumerable <string> EnumerateReferences(byte[] buffer, int startIndex, int length) { int endIndex = startIndex + length; int index = startIndex; InternetAddress addr; if (buffer == null) { throw new ArgumentNullException("buffer"); } if (startIndex < 0 || startIndex > buffer.Length) { throw new ArgumentOutOfRangeException("startIndex"); } if (length < 0 || startIndex + length > buffer.Length) { throw new ArgumentOutOfRangeException("length"); } do { if (!ParseUtils.SkipCommentsAndWhiteSpace(buffer, ref index, endIndex, false)) { break; } if (index >= endIndex) { break; } if (buffer[index] == '<') { if (!InternetAddress.TryParseMailbox(buffer, startIndex, ref index, endIndex, "", 65001, false, out addr)) { break; } yield return("<" + ((MailboxAddress)addr).Address + ">"); } else if (!ParseUtils.Skip8bitWord(buffer, ref index, endIndex, false)) { index++; } } while (index < endIndex); yield break; }
static IEnumerable <DateToken> TokenizeDate(byte[] text, int startIndex, int length) { int endIndex = startIndex + length; int index = startIndex; DateTokenFlags mask; int start; while (index < endIndex) { if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, false)) { break; } if (index >= endIndex) { break; } // get the initial mask for this token if ((mask = datetok[text[index]]) != DateTokenFlags.None) { start = index++; // find the end of this token while (index < endIndex && !IsTokenDelimeter(text[index])) { mask |= datetok[text[index++]]; } yield return(new DateToken(mask, start, index - start)); } // skip over the token delimeter index++; } yield break; }
/// <summary> /// Tries to parse a version from a header such as Mime-Version. /// </summary> /// <returns><c>true</c>, if the version was successfully parsed, <c>false</c> otherwise.</returns> /// <param name="buffer">The raw byte buffer to parse.</param> /// <param name="startIndex">The index into the buffer to start parsing.</param> /// <param name="length">The length of the buffer to parse.</param> /// <param name="version">The parsed version.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="buffer"/> is <c>null</c>. /// </exception> /// <exception cref="System.ArgumentOutOfRangeException"> /// <paramref name="startIndex"/> and <paramref name="length"/> do not specify /// a valid range in the byte array. /// </exception> public static bool TryParseVersion(byte[] buffer, int startIndex, int length, out Version version) { if (buffer == null) { throw new ArgumentNullException("buffer"); } if (startIndex < 0 || startIndex > buffer.Length) { throw new ArgumentOutOfRangeException("startIndex"); } if (length < 0 || startIndex + length > buffer.Length) { throw new ArgumentOutOfRangeException("length"); } List <int> values = new List <int> (); int endIndex = startIndex + length; int index = startIndex; int value; version = null; do { if (!ParseUtils.SkipCommentsAndWhiteSpace(buffer, ref index, endIndex, false) || index >= endIndex) { return(false); } if (!ParseUtils.TryParseInt32(buffer, ref index, endIndex, out value)) { return(false); } values.Add(value); if (!ParseUtils.SkipCommentsAndWhiteSpace(buffer, ref index, endIndex, false)) { return(false); } if (index >= endIndex) { break; } if (buffer[index++] != (byte)'.') { return(false); } } while (index < endIndex); switch (values.Count) { case 4: version = new Version(values[0], values[1], values[2], values[3]); break; case 3: version = new Version(values[0], values[1], values[2]); break; case 2: version = new Version(values[0], values[1]); break; default: return(false); } return(true); }
/// <summary> /// Parses a Message-Id header value. /// </summary> /// <remarks> /// Parses the Message-Id value, returning the addr-spec portion of the msg-id token. /// </remarks> /// <returns>The addr-spec portion of the msg-id token.</returns> /// <param name="buffer">The raw byte buffer to parse.</param> /// <param name="startIndex">The index into the buffer to start parsing.</param> /// <param name="length">The length of the buffer to parse.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="buffer"/> is <c>null</c>. /// </exception> /// <exception cref="System.ArgumentOutOfRangeException"> /// <paramref name="startIndex"/> and <paramref name="length"/> do not specify /// a valid range in the byte array. /// </exception> public static string ParseMessageId(byte[] buffer, int startIndex, int length) { byte[] sentinels = { (byte)'>' }; int endIndex = startIndex + length; int index = startIndex; string msgid; if (buffer == null) { throw new ArgumentNullException("buffer"); } if (startIndex < 0 || startIndex > buffer.Length) { throw new ArgumentOutOfRangeException("startIndex"); } if (length < 0 || length > (buffer.Length - startIndex)) { throw new ArgumentOutOfRangeException("length"); } if (!ParseUtils.SkipCommentsAndWhiteSpace(buffer, ref index, endIndex, false)) { return(null); } if (index >= endIndex) { return(null); } if (buffer[index] == '<') { // skip over the '<' index++; if (index >= endIndex) { return(null); } } string localpart; if (!InternetAddress.TryParseLocalPart(buffer, ref index, endIndex, false, out localpart)) { return(null); } if (index >= endIndex) { return(null); } if (buffer[index] == (byte)'>') { // The msgid token did not contain an @domain. Technically this is illegal, but for the // sake of maximum compatibility, I guess we have no choice but to accept it... return(localpart); } if (buffer[index] != (byte)'@') { // who the hell knows what we have here... return(null); } // skip over the '@' index++; if (!ParseUtils.SkipCommentsAndWhiteSpace(buffer, ref index, endIndex, false)) { return(null); } if (index >= endIndex) { return(null); } if (buffer[index] == (byte)'>') { // The msgid token was in the form "<local-part@>". Technically this is illegal, but for // the sake of maximum compatibility, I guess we have no choice but to accept it... // https://github.com/jstedfast/MimeKit/issues/102 return(localpart + "@"); } string domain; if (!ParseUtils.TryParseDomain(buffer, ref index, endIndex, sentinels, false, out domain)) { return(null); } msgid = localpart + "@" + domain; // Note: some Message-Id's are broken and in the form "<local-part@domain@domain>" // https://github.com/jstedfast/MailKit/issues/138 while (index < endIndex && buffer[index] == (byte)'@') { index++; if (!ParseUtils.TryParseDomain(buffer, ref index, endIndex, sentinels, false, out domain)) { break; } msgid += "@" + domain; } return(msgid); }
/// <summary> /// Enumerates the message-id references such as those that can be found in /// the In-Reply-To or References header. /// </summary> /// <remarks> /// Incrementally parses Message-Ids (such as those from a References header /// in a MIME message) from the supplied buffer starting at the given index /// and spanning across the specified number of bytes. /// </remarks> /// <returns>The references.</returns> /// <param name="buffer">The raw byte buffer to parse.</param> /// <param name="startIndex">The index into the buffer to start parsing.</param> /// <param name="length">The length of the buffer to parse.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="buffer"/> is <c>null</c>. /// </exception> /// <exception cref="System.ArgumentOutOfRangeException"> /// <paramref name="startIndex"/> and <paramref name="length"/> do not specify /// a valid range in the byte array. /// </exception> public static IEnumerable <string> EnumerateReferences(byte[] buffer, int startIndex, int length) { ParseUtils.ValidateArguments(buffer, startIndex, length); byte[] sentinels = { (byte)'>' }; int endIndex = startIndex + length; int index = startIndex; string msgid; do { if (!ParseUtils.SkipCommentsAndWhiteSpace(buffer, ref index, endIndex, false)) { break; } if (index >= endIndex) { break; } if (buffer[index] == '<') { // skip over the '<' index++; if (index >= endIndex) { break; } string localpart; if (!InternetAddress.TryParseLocalPart(buffer, ref index, endIndex, false, out localpart)) { continue; } if (index >= endIndex) { break; } if (buffer[index] == (byte)'>') { // The msgid token did not contain an @domain. Technically this is illegal, but for the // sake of maximum compatibility, I guess we have no choice but to accept it... index++; yield return(localpart); continue; } if (buffer[index] != (byte)'@') { // who the hell knows what we have here... ignore it and continue on? continue; } // skip over the '@' index++; if (!ParseUtils.SkipCommentsAndWhiteSpace(buffer, ref index, endIndex, false)) { break; } if (index >= endIndex) { break; } if (buffer[index] == (byte)'>') { // The msgid token was in the form "<local-part@>". Technically this is illegal, but for // the sake of maximum compatibility, I guess we have no choice but to accept it... // https://github.com/jstedfast/MimeKit/issues/102 index++; yield return(localpart + "@"); continue; } string domain; if (!ParseUtils.TryParseDomain(buffer, ref index, endIndex, sentinels, false, out domain)) { continue; } msgid = localpart + "@" + domain; // Note: some Message-Id's are broken and in the form "<local-part@domain@domain>" // https://github.com/jstedfast/MailKit/issues/138 while (index < endIndex && buffer[index] == (byte)'@') { int saved = index; index++; if (!ParseUtils.TryParseDomain(buffer, ref index, endIndex, sentinels, false, out domain)) { index = saved; break; } msgid += "@" + domain; } yield return(msgid); } else if (!ParseUtils.SkipWord(buffer, ref index, endIndex, false)) { index++; } } while (index < endIndex); yield break; }
/// <summary> /// Enumerates the message-id references such as those that can be found in /// the In-Reply-To or References header. /// </summary> /// <remarks> /// Incrementally parses Message-Ids (such as those from a References header /// in a MIME message) from the supplied buffer starting at the given index /// and spanning across the specified number of bytes. /// </remarks> /// <returns>The references.</returns> /// <param name="buffer">The raw byte buffer to parse.</param> /// <param name="startIndex">The index into the buffer to start parsing.</param> /// <param name="length">The length of the buffer to parse.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="buffer"/> is <c>null</c>. /// </exception> /// <exception cref="System.ArgumentOutOfRangeException"> /// <paramref name="startIndex"/> and <paramref name="length"/> do not specify /// a valid range in the byte array. /// </exception> public static IEnumerable <string> EnumerateReferences(byte[] buffer, int startIndex, int length) { byte[] sentinels = { (byte)'>' }; int endIndex = startIndex + length; int index = startIndex; InternetAddress addr; string msgid; if (buffer == null) { throw new ArgumentNullException("buffer"); } if (startIndex < 0 || startIndex > buffer.Length) { throw new ArgumentOutOfRangeException("startIndex"); } if (length < 0 || length > (buffer.Length - startIndex)) { throw new ArgumentOutOfRangeException("length"); } do { if (!ParseUtils.SkipCommentsAndWhiteSpace(buffer, ref index, endIndex, false)) { break; } if (index >= endIndex) { break; } if (buffer[index] == '<') { if (!InternetAddress.TryParseMailbox(ParserOptions.Default, buffer, startIndex, ref index, endIndex, "", 65001, false, out addr)) { break; } msgid = ((MailboxAddress)addr).Address; // Note: some message-id's are broken and in the form local-part@domain@domain // https://github.com/jstedfast/MailKit/issues/138 while (index < endIndex && buffer[index] == (byte)'@') { int saved = index; string domain; index++; if (!ParseUtils.TryParseDomain(buffer, ref index, endIndex, sentinels, false, out domain)) { index = saved; break; } msgid += "@" + domain; } yield return(msgid); } else if (!ParseUtils.SkipWord(buffer, ref index, endIndex, false)) { index++; } } while (index < endIndex); yield break; }