Exemple #1
0
        public async Task <MimePartSpan> ReadStructureAsync(Stream mailStream, CancellationToken cancellationToken)
        {
            using (UnencodedStreamReader reader = new UnencodedStreamReader(mailStream, leaveOpen: true))
            {
                MimePartBuilder topLevelBuilder = new MimePartBuilder();
                var             currentBuilder  = topLevelBuilder;

                StringBuilder currentHeader = new StringBuilder();
                while ((await ReadLine(reader, cancellationToken)).TryGet(out Memory <byte> line))
                {
                    long position = reader.BytePositition;
                    if (currentBuilder.Parent != null &&
                        currentBuilder.Parent.EndBoundary.Span.SequenceEqual(line.Span))
                    {
                        currentBuilder = currentBuilder.Parent;
                        continue;
                    }

                    if (currentBuilder.Parent != null &&
                        currentBuilder.Parent.Boundary.Span.SequenceEqual(line.Span))
                    {
                        currentBuilder = new MimePartBuilder(currentBuilder.Parent, position);
                        continue;
                    }

                    if (!currentBuilder.Boundary.IsEmpty &&
                        currentBuilder.Children == null &&
                        currentBuilder.Boundary.Span.SequenceEqual(line.Span))
                    {
                        currentBuilder.Children = new List <MimePartBuilder>();
                        currentBuilder          = new MimePartBuilder(currentBuilder, position);
                        continue;
                    }

                    currentBuilder.End = position;

                    if (!currentBuilder.HeaderComplete)
                    {
                        void ProcessPendingHeader()
                        {
                            if (currentHeader.Length == 0)
                            {
                                return;
                            }

                            string headerString = currentHeader.ToString();

                            currentHeader.Clear();
                            if (TestRegex(ContentTypeRegex, headerString, out var match))
                            {
                                string type = match.Groups["type"].Value;
                                if (type == "multipart")
                                {
                                    CaptureCollection paramCaptures = match.Groups["param"].Captures;
                                    CaptureCollection valueCaptures = match.Groups["value"].Captures;
                                    for (int i = 0; i < paramCaptures.Count; i++)
                                    {
                                        switch (paramCaptures[i].Value.ToLowerInvariant())
                                        {
                                        case "charset":
                                            break;

                                        case "boundary":
                                            ReadOnlySpan <char> value = valueCaptures[i].Value.AsSpan();
                                            if (value.Length > 1 &&
                                                value[0] == '"' &&
                                                value[value.Length - 1] == '"')
                                            {
                                                value = value.Slice(1, value.Length - 2);
                                                if (value.Contains("\\", StringComparison.Ordinal))
                                                {
                                                    value = Regex.Replace(value.ToString(), @"\\.", m => m.Value[1].ToString());
                                                }
                                            }

                                            currentBuilder.EndBoundary =
                                                Encoding.ASCII.GetBytes("--" + value.ToString() + "--");
                                            break;
                                        }
                                    }
                                }
                            }
                        }

                        if (line.Length == 0)
                        {
                            // header's over, process pending ones
                            ProcessPendingHeader();
                            currentBuilder.ContentStart = position;
                            continue;
                        }

                        if (IsImportantHeader(line))
                        {
                            currentHeader.Append(Encoding.ASCII.GetString(line.Span));
                            continue;
                        }

                        if (currentHeader.Length > 0)
                        {
                            byte first = line.Span[0];
                            if (first == ' ' || first == '\t')
                            {
                                currentHeader.Append(Encoding.ASCII.GetString(line.Span));
                                continue;
                            }

                            // We found another header, process the current one.
                            ProcessPendingHeader();

                            if (IsImportantHeader(line))
                            {
                                currentHeader.Append(Encoding.ASCII.GetString(line.Span));
                            }
                        }
                    }
                }

                currentBuilder.End = reader.BytePositition;

                return(topLevelBuilder.ToMimePart());
            }
        }
Exemple #2
0
 public MimePartBuilder(MimePartBuilder parent, long position)
 {
     Parent = parent;
     Start  = position;
     parent?.Children.Add(this);
 }