/// <summary> /// Parses mime entity from stream. /// </summary> /// <param name="stream">Data stream from where to read data.</param> /// <param name="toBoundary">Entity data is readed to specified boundary.</param> /// <returns>Returns false if last entity. Returns true for mulipart entity, if there are more entities.</returns> internal bool Parse(SmartStream stream, string toBoundary) { // Clear header fields m_pHeader.Clear(); m_pHeaderFieldCache.Clear(); // Parse header m_pHeader.Parse(stream); // Parse entity and child entities if any (Conent-Type: multipart/xxx...) // Multipart entity if ((ContentType & MediaType_enum.Multipart) != 0) { // There must be be boundary ID (rfc 1341 7.2.1 The Content-Type field for multipart entities requires one parameter, // "boundary", which is used to specify the encapsulation boundary.) string boundaryID = ContentType_Boundary; if (boundaryID == null) { // This is invalid message, just skip this mime entity } else { // There is one or more mime entities // Find first boundary start position SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[8000], SizeExceededAction. JunkAndThrowException); stream.ReadLine(args, false); if (args.Error != null) { throw args.Error; } string lineString = args.LineUtf8; while (lineString != null) { if (lineString.StartsWith("--" + boundaryID)) { break; } stream.ReadLine(args, false); if (args.Error != null) { throw args.Error; } lineString = args.LineUtf8; } // This is invalid entity, boundary start not found. Skip that entity. if (string.IsNullOrEmpty(lineString)) { return false; } // Start parsing child entities of this entity while (true) { // Parse and add child entity MimeEntity childEntity = new MimeEntity(); ChildEntities.Add(childEntity); // This is last entity, stop parsing if (childEntity.Parse(stream, boundaryID) == false) { break; } // else{ // There are more entities, parse them } // This entity is child of mulipart entity. // All this entity child entities are parsed, // we need to move stream position to next entity start. if (!string.IsNullOrEmpty(toBoundary)) { stream.ReadLine(args, false); if (args.Error != null) { throw args.Error; } lineString = args.LineUtf8; while (lineString != null) { if (lineString.StartsWith("--" + toBoundary)) { break; } stream.ReadLine(args, false); if (args.Error != null) { throw args.Error; } lineString = args.LineUtf8; } // Invalid boundary end, there can't be more entities if (string.IsNullOrEmpty(lineString)) { return false; } // See if last boundary or there is more. Last boundary ends with -- if (lineString.EndsWith(toBoundary + "--")) { return false; } // else{ // There are more entities return true; } } } // Singlepart entity. else { // Boundary is specified, read data to specified boundary. if (!string.IsNullOrEmpty(toBoundary)) { MemoryStream entityData = new MemoryStream(); SmartStream.ReadLineAsyncOP readLineOP = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength], SizeExceededAction . JunkAndThrowException); // Read entity data while get boundary end tag --boundaryID-- or EOS. while (true) { stream.ReadLine(readLineOP, false); if (readLineOP.Error != null) { throw readLineOP.Error; } // End of stream reached. Normally we should get boundary end tag --boundaryID--, but some x mailers won't add it, so // if we reach EOS, consider boundary closed. if (readLineOP.BytesInBuffer == 0) { // Just return data what was readed. m_EncodedData = entityData.ToArray(); return false; } // We readed a line. else { // We have boundary start/end tag or just "--" at the beginning of line. if (readLineOP.LineBytesInBuffer >= 2 && readLineOP.Buffer[0] == '-' && readLineOP.Buffer[1] == '-') { string lineString = readLineOP.LineUtf8; // We have boundary end tag, no more boundaries. if (lineString == "--" + toBoundary + "--") { m_EncodedData = entityData.ToArray(); return false; } // We have new boundary start. else if (lineString == "--" + toBoundary) { m_EncodedData = entityData.ToArray(); return true; } else { // Just skip } } // Write readed line. entityData.Write(readLineOP.Buffer, 0, readLineOP.BytesInBuffer); } } } // Boundary isn't specified, read data to the stream end. else { MemoryStream ms = new MemoryStream(); stream.ReadAll(ms); m_EncodedData = ms.ToArray(); } } return false; }