async Task MultipartScanSubpartsAsync(Multipart multipart, int depth, CancellationToken cancellationToken) { //var beginOffset = GetOffset (inputIndex); do { //OnMultipartBoundaryBegin (multipart, beginOffset); // skip over the boundary marker if (!await SkipLineAsync(true, cancellationToken).ConfigureAwait(false)) { //OnMultipartBoundaryEnd (multipart, GetOffset (inputIndex)); boundary = BoundaryType.Eos; return; } //OnMultipartBoundaryEnd (multipart, GetOffset (inputIndex)); var beginLineNumber = lineNumber; // parse the headers state = MimeParserState.Headers; if (await StepAsync(cancellationToken).ConfigureAwait(false) == MimeParserState.Error) { boundary = BoundaryType.Eos; return; } if (state == MimeParserState.Boundary) { if (headers.Count == 0) { if (boundary == BoundaryType.ImmediateBoundary) { //beginOffset = GetOffset (inputIndex); continue; } return; } // This part has no content, but that will be handled in ConstructMultipartAsync() // or ConstructMimePartAsync(). } //if (state == ParserState.Complete && headers.Count == 0) // return BoundaryType.EndBoundary; var type = GetContentType(multipart.ContentType); var entity = options.CreateEntity(type, headers, false, depth); var entityArgs = new MimeEntityEndEventArgs(entity, multipart) { HeadersEndOffset = headerBlockEnd, BeginOffset = headerBlockBegin, LineNumber = beginLineNumber }; OnMimeEntityBegin(entityArgs); if (entity is Multipart) { await ConstructMultipartAsync((Multipart)entity, entityArgs, depth + 1, cancellationToken).ConfigureAwait(false); } else if (entity is MessagePart) { await ConstructMessagePartAsync((MessagePart)entity, entityArgs, depth + 1, cancellationToken).ConfigureAwait(false); } else { await ConstructMimePartAsync((MimePart)entity, entityArgs, cancellationToken).ConfigureAwait(false); } var endOffset = GetEndOffset(inputIndex); entityArgs.HeadersEndOffset = Math.Min(entityArgs.HeadersEndOffset, endOffset); entityArgs.EndOffset = endOffset; OnMimeEntityEnd(entityArgs); //beginOffset = endOffset; multipart.Add(entity); } while (boundary == BoundaryType.ImmediateBoundary); }
async Task ConstructMultipartAsync(Multipart multipart, MimeEntityEndEventArgs args, int depth, CancellationToken cancellationToken) { var beginOffset = GetOffset(inputIndex); var beginLineNumber = lineNumber; var marker = multipart.Boundary; long endOffset; if (marker == null) { #if DEBUG Debug.WriteLine("Multipart without a boundary encountered!"); #endif // Note: this will scan all content into the preamble... await MultipartScanPreambleAsync(multipart, cancellationToken).ConfigureAwait(false); endOffset = GetEndOffset(inputIndex); args.Lines = GetLineCount(beginLineNumber, beginOffset, endOffset); return; } PushBoundary(marker); await MultipartScanPreambleAsync(multipart, cancellationToken).ConfigureAwait(false); if (boundary == BoundaryType.ImmediateBoundary) { await MultipartScanSubpartsAsync(multipart, depth, cancellationToken).ConfigureAwait(false); } if (boundary == BoundaryType.ImmediateEndBoundary) { //OnMultipartEndBoundaryBegin (multipart, GetEndOffset (inputIndex)); // consume the end boundary and read the epilogue (if there is one) multipart.WriteEndBoundary = true; await SkipLineAsync(false, cancellationToken).ConfigureAwait(false); PopBoundary(); //OnMultipartEndBoundaryEnd (multipart, GetOffset (inputIndex)); await MultipartScanEpilogueAsync(multipart, cancellationToken).ConfigureAwait(false); endOffset = GetEndOffset(inputIndex); args.Lines = GetLineCount(beginLineNumber, beginOffset, endOffset); return; } endOffset = GetEndOffset(inputIndex); args.Lines = GetLineCount(beginLineNumber, beginOffset, endOffset); multipart.WriteEndBoundary = false; // We either found the end of the stream or we found a parent's boundary PopBoundary(); unsafe { fixed(byte *inbuf = input) { if (boundary == BoundaryType.ParentEndBoundary && FoundImmediateBoundary(inbuf, true)) { boundary = BoundaryType.ImmediateEndBoundary; } else if (boundary == BoundaryType.ParentBoundary && FoundImmediateBoundary(inbuf, false)) { boundary = BoundaryType.ImmediateBoundary; } } } }
async Task ConstructMessagePartAsync(MessagePart rfc822, MimeEntityEndEventArgs args, int depth, CancellationToken cancellationToken) { var beginOffset = GetOffset(inputIndex); var beginLineNumber = lineNumber; if (bounds.Count > 0) { int atleast = Math.Max(ReadAheadSize, GetMaxBoundaryLength()); if (await ReadAheadAsync(atleast, 0, cancellationToken).ConfigureAwait(false) <= 0) { boundary = BoundaryType.Eos; return; } unsafe { fixed(byte *inbuf = input) { byte *start = inbuf + inputIndex; byte *inend = inbuf + inputEnd; byte *inptr = start; *inend = (byte)'\n'; while (*inptr != (byte)'\n') { inptr++; } boundary = CheckBoundary(inputIndex, start, (int)(inptr - start)); switch (boundary) { case BoundaryType.ImmediateEndBoundary: case BoundaryType.ImmediateBoundary: case BoundaryType.ParentBoundary: return; case BoundaryType.ParentEndBoundary: // ignore "From " boundaries, broken mailers tend to include these... if (!IsMboxMarker(start)) { return; } break; } } } } // parse the headers... state = MimeParserState.MessageHeaders; if (await StepAsync(cancellationToken).ConfigureAwait(false) == MimeParserState.Error) { // Note: this either means that StepHeaders() found the end of the stream // or an invalid header field name at the start of the message headers, // which likely means that this is not a valid MIME stream? boundary = BoundaryType.Eos; return; } var message = new MimeMessage(options, headers, RfcComplianceMode.Loose); var messageArgs = new MimeMessageEndEventArgs(message, rfc822) { HeadersEndOffset = headerBlockEnd, BeginOffset = headerBlockBegin, LineNumber = beginLineNumber }; OnMimeMessageBegin(messageArgs); if (preHeaderBuffer.Length > 0) { message.MboxMarker = new byte[preHeaderLength]; Buffer.BlockCopy(preHeaderBuffer, 0, message.MboxMarker, 0, preHeaderLength); } var type = GetContentType(null); var entity = options.CreateEntity(type, headers, true, depth); var entityArgs = new MimeEntityEndEventArgs(entity) { HeadersEndOffset = headerBlockEnd, BeginOffset = headerBlockBegin, LineNumber = beginLineNumber }; OnMimeEntityBegin(entityArgs); message.Body = entity; if (entity is Multipart) { await ConstructMultipartAsync((Multipart)entity, entityArgs, depth + 1, cancellationToken).ConfigureAwait(false); } else if (entity is MessagePart) { await ConstructMessagePartAsync((MessagePart)entity, entityArgs, depth + 1, cancellationToken).ConfigureAwait(false); } else { await ConstructMimePartAsync((MimePart)entity, entityArgs, cancellationToken).ConfigureAwait(false); } rfc822.Message = message; var endOffset = GetEndOffset(inputIndex); messageArgs.HeadersEndOffset = entityArgs.HeadersEndOffset = Math.Min(entityArgs.HeadersEndOffset, endOffset); messageArgs.EndOffset = entityArgs.EndOffset = endOffset; OnMimeEntityEnd(entityArgs); OnMimeMessageEnd(messageArgs); args.Lines = GetLineCount(beginLineNumber, beginOffset, endOffset); }