public void TestInvalidAtNoDomainMessageId() { // https://github.com/jstedfast/MimeKit/issues/102 var msgid = MimeUtils.EnumerateReferences("<local-part@>").FirstOrDefault(); Assert.AreEqual("local-part@", msgid); }
public void TestArgumentExceptions() { var buffer = new byte[1024]; Assert.Throws <ArgumentNullException> (() => MimeUtils.GenerateMessageId(null), "MimeUtils.GenerateMessageId (null)"); Assert.Throws <ArgumentException> (() => MimeUtils.GenerateMessageId(string.Empty), "MimeUtils.GenerateMessageId (string.Empty)"); Assert.Throws <ArgumentNullException> (() => MimeUtils.EnumerateReferences(null), "MimeUtils.EnumerateReferences (null)"); Assert.Throws <ArgumentNullException> (() => MimeUtils.EnumerateReferences(null, 0, 0).FirstOrDefault(), "MimeUtils.EnumerateReferences (null, 0, 0)"); Assert.Throws <ArgumentOutOfRangeException> (() => MimeUtils.EnumerateReferences(buffer, -1, 0).FirstOrDefault(), "MimeUtils.EnumerateReferences (buffer, -1, 0)"); Assert.Throws <ArgumentOutOfRangeException> (() => MimeUtils.EnumerateReferences(buffer, buffer.Length + 1, 0).FirstOrDefault(), "MimeUtils.EnumerateReferences (buffer, buffer.Length + 1, 0)"); Assert.Throws <ArgumentOutOfRangeException> (() => MimeUtils.EnumerateReferences(buffer, 0, -1).FirstOrDefault(), "MimeUtils.EnumerateReferences (buffer, 0, -1)"); Assert.Throws <ArgumentOutOfRangeException> (() => MimeUtils.EnumerateReferences(buffer, 0, buffer.Length + 1).FirstOrDefault(), "MimeUtils.EnumerateReferences (buffer, 0, buffer.Length + 1)"); Assert.Throws <ArgumentNullException> (() => MimeUtils.ParseMessageId(null), "MimeUtils.ParseMessageId (null)"); Assert.Throws <ArgumentNullException> (() => MimeUtils.ParseMessageId(null, 0, 0), "MimeUtils.ParseMessageId (null, 0, 0)"); Assert.Throws <ArgumentOutOfRangeException> (() => MimeUtils.ParseMessageId(buffer, -1, 0), "MimeUtils.ParseMessageId (buffer, -1, 0)"); Assert.Throws <ArgumentOutOfRangeException> (() => MimeUtils.ParseMessageId(buffer, buffer.Length + 1, 0), "MimeUtils.ParseMessageId (buffer, buffer.Length + 1, 0)"); Assert.Throws <ArgumentOutOfRangeException> (() => MimeUtils.ParseMessageId(buffer, 0, -1), "MimeUtils.ParseMessageId (buffer, 0, -1)"); Assert.Throws <ArgumentOutOfRangeException> (() => MimeUtils.ParseMessageId(buffer, 0, buffer.Length + 1), "MimeUtils.ParseMessageId (buffer, 0, buffer.Length + 1)"); Assert.Throws <ArgumentNullException> (() => MimeUtils.AppendQuoted(null, "text"), "MimeUtils.AppendQuoted (null, value)"); Assert.Throws <ArgumentNullException> (() => MimeUtils.AppendQuoted(new StringBuilder(), null), "MimeUtils.AppendQuoted (builder, null)"); Assert.Throws <ArgumentNullException> (() => MimeUtils.Quote(null), "MimeUtils.Quote (null)"); Assert.Throws <ArgumentNullException> (() => MimeUtils.Unquote(null), "MimeUtils.Unquote (null)"); }
/// <summary> /// Parses the ENVELOPE parenthesized list. /// </summary> /// <returns>The envelope.</returns> /// <param name="engine">The IMAP engine.</param> /// <param name="cancellationToken">The cancellation token.</param> public static Envelope ParseEnvelope(ImapEngine engine, CancellationToken cancellationToken) { var token = engine.ReadToken (cancellationToken); string nstring; if (token.Type != ImapTokenType.OpenParen) throw ImapEngine.UnexpectedToken (token, false); var envelope = new Envelope (); envelope.Date = ParseEnvelopeDate (engine, cancellationToken); envelope.Subject = ReadNStringToken (engine, true, cancellationToken); ParseEnvelopeAddressList (envelope.From, engine, cancellationToken); ParseEnvelopeAddressList (envelope.Sender, engine, cancellationToken); ParseEnvelopeAddressList (envelope.ReplyTo, engine, cancellationToken); ParseEnvelopeAddressList (envelope.To, engine, cancellationToken); ParseEnvelopeAddressList (envelope.Cc, engine, cancellationToken); ParseEnvelopeAddressList (envelope.Bcc, engine, cancellationToken); if ((nstring = ReadNStringToken (engine, false, cancellationToken)) != null) envelope.InReplyTo = MimeUtils.EnumerateReferences (nstring).FirstOrDefault (); if ((nstring = ReadNStringToken (engine, false, cancellationToken)) != null) envelope.MessageId = MimeUtils.EnumerateReferences (nstring).FirstOrDefault (); token = engine.ReadToken (cancellationToken); if (token.Type != ImapTokenType.CloseParen) throw ImapEngine.UnexpectedToken (token, false); return envelope; }
static byte[] EncodeReferencesHeader(ParserOptions options, FormatOptions format, Encoding charset, string field, string value) { var encoded = new StringBuilder(); int lineLength = field.Length + 1; int count = 0; foreach (var reference in MimeUtils.EnumerateReferences(value)) { if (count > 0 && lineLength + reference.Length + 3 > format.MaxLineLength) { encoded.Append(format.NewLine); encoded.Append('\t'); lineLength = 1; count = 0; } else { encoded.Append(' '); lineLength++; } encoded.Append('<').Append(reference).Append('>'); lineLength += reference.Length + 2; count++; } encoded.Append(format.NewLine); return(charset.GetBytes(encoded.ToString())); }
MessageSummary MakeThreadable(ref int index, string subject, string msgid, string date, string refs) { DateTimeOffset value; DateUtils.TryParse(date, out value); var summary = new MessageSummary(index++); summary.UniqueId = new UniqueId((uint)summary.Index); summary.Envelope = new Envelope(); summary.References = new MessageIdList(); if (refs != null) { foreach (var id in refs.Split(new [] { ' ' }, StringSplitOptions.RemoveEmptyEntries)) { summary.References.Add(id); } } summary.Envelope.MessageId = MimeUtils.EnumerateReferences(msgid).FirstOrDefault(); summary.Envelope.Subject = subject; summary.Envelope.Date = value; summary.Size = 0; return(summary); }
/// <summary> /// Called when the headers change in some way. /// </summary> /// <remarks> /// Whenever a header is changed, this method will be called in order to allow /// custom <see cref="MimeEntity"/> subclasses to update their state. /// </remarks> /// <param name="action">The type of change.</param> /// <param name="header">The header being added, changed or removed.</param> protected virtual void OnHeadersChanged(HeaderListChangedAction action, Header header) { switch (action) { case HeaderListChangedAction.Added: case HeaderListChangedAction.Changed: switch (header.Id) { case HeaderId.ContentDisposition: if (disposition != null) { disposition.Changed -= ContentDispositionChanged; } if (ContentDisposition.TryParse(Headers.Options, header.RawValue, out disposition)) { disposition.Changed += ContentDispositionChanged; } break; case HeaderId.ContentId: contentId = MimeUtils.EnumerateReferences(header.RawValue, 0, header.RawValue.Length).FirstOrDefault(); break; } break; case HeaderListChangedAction.Removed: switch (header.Id) { case HeaderId.ContentDisposition: if (disposition != null) { disposition.Changed -= ContentDispositionChanged; } disposition = null; break; case HeaderId.ContentId: contentId = null; break; } break; case HeaderListChangedAction.Cleared: if (disposition != null) { disposition.Changed -= ContentDispositionChanged; } disposition = null; contentId = null; break; default: throw new ArgumentOutOfRangeException("action"); } }
/// <summary> /// Gets the index of the body part matching the specified URI. /// </summary> /// <remarks> /// <para>Finds the index of the body part matching the specified URI, if it exists.</para> /// <para>If the URI scheme is <c>"cid"</c>, then matching is performed based on the Content-Id header /// values, otherwise the Content-Location headers are used. If the provided URI is absolute and a child /// part's Content-Location is relative, then then the child part's Content-Location URI will be combined /// with the value of its Content-Base header, if available, otherwise it will be combined with the /// multipart/related part's Content-Base header in order to produce an absolute URI that can be /// compared with the provided absolute URI.</para> /// </remarks> /// <returns>The index of the part matching the specified URI if found; otherwise <c>-1</c>.</returns> /// <param name="uri">The URI of the body part.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="uri"/> is <c>null</c>. /// </exception> public int IndexOf(Uri uri) { if (uri == null) { throw new ArgumentNullException("uri"); } bool cid = uri.IsAbsoluteUri && uri.Scheme.ToLowerInvariant() == "cid"; for (int index = 0; index < Count; index++) { var bodyPart = this[index] as BodyPartBasic; if (bodyPart == null) { continue; } if (uri.IsAbsoluteUri) { if (cid) { if (!string.IsNullOrEmpty(bodyPart.ContentId)) { var id = MimeUtils.EnumerateReferences(bodyPart.ContentId).FirstOrDefault(); if (id == uri.AbsolutePath) { return(index); } } } else if (bodyPart.ContentLocation != null) { Uri absolute; if (!bodyPart.ContentLocation.IsAbsoluteUri) { continue; } absolute = bodyPart.ContentLocation; if (absolute == uri) { return(index); } } } else if (bodyPart.ContentLocation == uri) { return(index); } } return(-1); }
/// <summary> /// Gets the index of the body part matching the specified URI. /// </summary> /// <remarks> /// <para>Finds the index of the body part matching the specified URI, if it exists.</para> /// <para>If the URI scheme is <c>"cid"</c>, then matching is performed based on the Content-Id header /// values, otherwise the Content-Location headers are used. If the provided URI is absolute and a child /// part's Content-Location is relative, then then the child part's Content-Location URI will be combined /// with the value of its Content-Base header, if available, otherwise it will be combined with the /// multipart/related part's Content-Base header in order to produce an absolute URI that can be /// compared with the provided absolute URI.</para> /// </remarks> /// <returns>The index of the part matching the specified URI if found; otherwise <c>-1</c>.</returns> /// <param name="uri">The URI of the body part.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="uri"/> is <c>null</c>. /// </exception> public int IndexOf(Uri uri) { if (uri == null) { throw new ArgumentNullException(nameof(uri)); } bool cid = uri.IsAbsoluteUri && uri.Scheme.ToLowerInvariant() == "cid"; for (int index = 0; index < Count; index++) { var bodyPart = this[index] as BodyPartBasic; if (bodyPart == null) { continue; } if (uri.IsAbsoluteUri) { if (cid) { if (!string.IsNullOrEmpty(bodyPart.ContentId)) { // Note: we might have a Content-Id in the form "<*****@*****.**>", so attempt to decode it var id = MimeUtils.EnumerateReferences(bodyPart.ContentId).FirstOrDefault() ?? bodyPart.ContentId; if (id == uri.AbsolutePath) { return(index); } } } else if (bodyPart.ContentLocation != null) { if (!bodyPart.ContentLocation.IsAbsoluteUri) { continue; } if (bodyPart.ContentLocation == uri) { return(index); } } } else if (bodyPart.ContentLocation == uri) { return(index); } } return(-1); }
public void TestParseGoodReferences() { for (int i = 0; i < GoodReferences.Length; i += 2) { var reference = MimeUtils.EnumerateReferences(GoodReferences[i]).FirstOrDefault(); Assert.AreEqual(GoodReferences[i + 1], reference, "Incorrectly parsed reference '{0}'.", GoodReferences[i]); reference = MimeUtils.ParseMessageId(GoodReferences[i]); Assert.AreEqual(GoodReferences[i + 1], reference, "Incorrectly parsed message-id '{0}'.", GoodReferences[i]); } }
public void TestParseBrokenReferences() { for (int i = 0; i < BrokenReferences.Length; i++) { var reference = MimeUtils.EnumerateReferences(BrokenReferences[i]).FirstOrDefault(); Assert.IsNull(reference, "MimeUtils.EnumerateReferences(\"{0}\")", BrokenReferences[i]); reference = MimeUtils.ParseMessageId(BrokenReferences[i]); Assert.IsNull(reference, "MimeUtils.ParseMessageId (\"{0}\")", BrokenReferences[i]); } }
public void TestParsingObsoleteInReplyToSyntax() { var obsolete = "Joe Sixpack's message sent on Mon, 17 Jan 1994 11:14:55 -0500 <*****@*****.**>"; var msgid = MimeUtils.EnumerateReferences(obsolete).FirstOrDefault(); Assert.IsNotNull(msgid, "The parsed msgid token should not be null"); Assert.AreEqual("*****@*****.**", msgid, "The parsed msgid does not match"); obsolete = "<*****@*****.**> as sent on Mon, 17 Jan 1994 11:14:55 -0500"; msgid = MimeUtils.EnumerateReferences(obsolete).FirstOrDefault(); Assert.IsNotNull(msgid, "The parsed msgid token should not be null"); Assert.AreEqual("*****@*****.**", msgid, "The parsed msgid does not match"); }
int GetRootIndex() { string start = ContentType.Parameters["start"]; if (start == null) { return(-1); } string contentId; if ((contentId = MimeUtils.EnumerateReferences(start).FirstOrDefault()) == null) { contentId = start; } var cid = new Uri(string.Format("cid:{0}", contentId)); return(IndexOf(cid)); }
int GetRootIndex() { var start = ContentType.Parameters["start"]; if (start != null) { string contentId; if ((contentId = MimeUtils.EnumerateReferences(start).FirstOrDefault()) == null) { contentId = start; } var cid = new Uri(string.Format("cid:{0}", contentId)); return(IndexOf(cid)); } var type = ContentType.Parameters["type"]; if (type == null) { return(-1); } for (int index = 0; index < Count; index++) { var mimeType = this[index].ContentType.MimeType; if (mimeType.Equals(type, StringComparison.OrdinalIgnoreCase)) { return(index); } } return(-1); }
static BodyPart GetMultipartRelatedRoot(BodyPartMultipart related) { string start = related.ContentType.Parameters["start"]; string contentId; if (start == null) { return(related.BodyParts.Count > 0 ? related.BodyParts[0] : null); } if ((contentId = MimeUtils.EnumerateReferences(start).FirstOrDefault()) == null) { contentId = start; } var cid = new Uri(string.Format("cid:{0}", contentId)); for (int i = 0; i < related.BodyParts.Count; i++) { var basic = related.BodyParts[i] as BodyPartBasic; if (basic != null && (basic.ContentId == contentId || basic.ContentLocation == cid)) { return(basic); } var multipart = related.BodyParts[i] as BodyPartMultipart; if (multipart != null && multipart.ContentLocation == cid) { return(multipart); } } return(null); }
void ReloadHeader(HeaderId id, string field) { if (id == HeaderId.Unknown) { return; } if (id == HeaderId.References) { references.Changed -= ReferencesChanged; references.Clear(); references.Changed += ReferencesChanged; } else if (id == HeaderId.InReplyTo) { inreplyto = null; } foreach (var header in Headers) { if (header.Id != id) { continue; } switch (id) { case HeaderId.MimeVersion: if (MimeUtils.TryParseVersion(header.RawValue, 0, header.RawValue.Length, out version)) { return; } break; case HeaderId.References: references.Changed -= ReferencesChanged; foreach (var msgid in MimeUtils.EnumerateReferences(header.RawValue, 0, header.RawValue.Length)) { references.Add(msgid); } references.Changed += ReferencesChanged; break; case HeaderId.InReplyTo: inreplyto = MimeUtils.EnumerateReferences(header.RawValue, 0, header.RawValue.Length).FirstOrDefault(); break; case HeaderId.MessageId: messageId = MimeUtils.EnumerateReferences(header.RawValue, 0, header.RawValue.Length).FirstOrDefault(); if (messageId != null) { return; } break; case HeaderId.Date: if (DateUtils.TryParseDateTime(header.RawValue, 0, header.RawValue.Length, out date)) { return; } break; } } }
public void TestInvalidNoDomainMessageId() { var msgid = MimeUtils.EnumerateReferences("<local-part>").FirstOrDefault(); Assert.AreEqual("local-part", msgid); }
/// <summary> /// Called when the headers change in some way. /// </summary> /// <remarks> /// <para>Whenever a header is added, changed, or removed, this method will /// be called in order to allow custom <see cref="MimeEntity"/> subclasses /// to update their state.</para> /// <para>Overrides of this method should call the base method so that their /// superclass may also update its own state.</para> /// </remarks> /// <param name="action">The type of change.</param> /// <param name="header">The header being added, changed or removed.</param> protected virtual void OnHeadersChanged(HeaderListChangedAction action, Header header) { string text; switch (action) { case HeaderListChangedAction.Added: case HeaderListChangedAction.Changed: switch (header.Id) { case HeaderId.ContentDisposition: if (disposition != null) { disposition.Changed -= ContentDispositionChanged; } if (ContentDisposition.TryParse(Headers.Options, header.RawValue, out disposition)) { disposition.Changed += ContentDispositionChanged; } break; case HeaderId.ContentLocation: text = header.Value.Trim(); if (Uri.IsWellFormedUriString(text, UriKind.Absolute)) { location = new Uri(text, UriKind.Absolute); } else if (Uri.IsWellFormedUriString(text, UriKind.Relative)) { location = new Uri(text, UriKind.Relative); } else { location = null; } break; case HeaderId.ContentBase: text = header.Value.Trim(); if (Uri.IsWellFormedUriString(text, UriKind.Absolute)) { baseUri = new Uri(text, UriKind.Absolute); } else { baseUri = null; } break; case HeaderId.ContentId: contentId = MimeUtils.EnumerateReferences(header.RawValue, 0, header.RawValue.Length).FirstOrDefault(); break; } break; case HeaderListChangedAction.Removed: switch (header.Id) { case HeaderId.ContentDisposition: if (disposition != null) { disposition.Changed -= ContentDispositionChanged; } disposition = null; break; case HeaderId.ContentLocation: location = null; break; case HeaderId.ContentBase: baseUri = null; break; case HeaderId.ContentId: contentId = null; break; } break; case HeaderListChangedAction.Cleared: if (disposition != null) { disposition.Changed -= ContentDispositionChanged; } disposition = null; contentId = null; location = null; baseUri = null; break; default: throw new ArgumentOutOfRangeException("action"); } }
internal static bool TryParse(string text, ref int index, out Envelope envelope) { InternetAddressList from, sender, replyto, to, cc, bcc; string inreplyto, messageid, subject, nstring; DateTimeOffset?date = null; envelope = null; while (index < text.Length && text[index] == ' ') { index++; } if (index >= text.Length || text[index] != '(') { if (index + 3 <= text.Length && text.Substring(index, 3) == "NIL") { index += 3; return(true); } return(false); } index++; if (!TryParse(text, ref index, out nstring)) { return(false); } if (nstring != null) { DateTimeOffset value; if (!DateUtils.TryParse(nstring, out value)) { return(false); } date = value; } if (!TryParse(text, ref index, out subject)) { return(false); } if (!TryParse(text, ref index, out from)) { return(false); } if (!TryParse(text, ref index, out sender)) { return(false); } if (!TryParse(text, ref index, out replyto)) { return(false); } if (!TryParse(text, ref index, out to)) { return(false); } if (!TryParse(text, ref index, out cc)) { return(false); } if (!TryParse(text, ref index, out bcc)) { return(false); } if (!TryParse(text, ref index, out inreplyto)) { return(false); } if (!TryParse(text, ref index, out messageid)) { return(false); } if (index >= text.Length || text[index] != ')') { return(false); } index++; envelope = new Envelope { Date = date, Subject = subject, From = from, Sender = sender, ReplyTo = replyto, To = to, Cc = cc, Bcc = bcc, InReplyTo = inreplyto != null?MimeUtils.EnumerateReferences(inreplyto).FirstOrDefault() ?? inreplyto : null, MessageId = messageid != null?MimeUtils.EnumerateReferences(messageid).FirstOrDefault() ?? messageid : null }; return(true); }
void HeadersChanged(object sender, HeaderListChangedEventArgs e) { InternetAddressList list; switch (e.Action) { case HeaderListChangedAction.Added: if (addresses.TryGetValue(e.Header.Field, out list)) { AddAddresses(e.Header, list); break; } switch (e.Header.Id) { case HeaderId.MimeVersion: MimeUtils.TryParseVersion(e.Header.RawValue, 0, e.Header.RawValue.Length, out version); break; case HeaderId.References: references.Changed -= ReferencesChanged; foreach (var msgid in MimeUtils.EnumerateReferences(e.Header.RawValue, 0, e.Header.RawValue.Length)) { references.Add(msgid); } references.Changed += ReferencesChanged; break; case HeaderId.InReplyTo: inreplyto = MimeUtils.EnumerateReferences(e.Header.RawValue, 0, e.Header.RawValue.Length).FirstOrDefault(); break; case HeaderId.MessageId: messageId = MimeUtils.EnumerateReferences(e.Header.RawValue, 0, e.Header.RawValue.Length).FirstOrDefault(); break; case HeaderId.Date: DateUtils.TryParseDateTime(e.Header.RawValue, 0, e.Header.RawValue.Length, out date); break; } break; case HeaderListChangedAction.Changed: case HeaderListChangedAction.Removed: if (addresses.TryGetValue(e.Header.Field, out list)) { ReloadAddressList(e.Header.Field, list); break; } ReloadHeader(e.Header.Id, e.Header.Field); break; case HeaderListChangedAction.Cleared: foreach (var kvp in addresses) { kvp.Value.Changed -= InternetAddressListChanged; kvp.Value.Clear(); kvp.Value.Changed += InternetAddressListChanged; } references.Changed -= ReferencesChanged; references.Clear(); references.Changed += ReferencesChanged; inreplyto = null; messageId = null; version = null; break; default: throw new ArgumentOutOfRangeException(); } }