/// <summary> /// /// </summary> /// <param name="data">The byte array in which to search for boundaries</param> /// <param name="boundary">The boundary string, from the Content-Type header</param> /// <param name="startIndex">The 0-based index to begin the search at (default is 0)</param> /// <returns>Index of the start of the next boundary line, or -1 if no such line exists</returns> public static MimePart[] findParts(byte[] data, String boundary, int startIndex = 0) { List <MimePart> parts = new List <MimePart>(); MimePart part = null; int lineStart = startIndex - 1; int start = -1; // First, we have to find the start boundary. // Scan the body for lines long enough to contain the boundary marker. while (lineStart < (data.Length - boundary.Length)) { // Find the next line int lineEnd = Array.IndexOf <byte>(data, (byte)'\n', ++lineStart); if (-1 == lineEnd) { // We found the end (instead of a newline); treat the whole thing as a line. lineEnd = data.Length; } // Make sure the line is long enough to hold the boundary if ((lineEnd - lineStart) >= boundary.Length) { // Found a long-enough line; Stringify it and check. String line = Encoding.UTF8.GetString( data, lineStart, (lineEnd - lineStart - 1)); if (line.Contains(boundary)) { // Check if this is the first boundary or a subsequent one. if (-1 == start) { // OK, we found the first boundary! Remember it and find the next. start = lineEnd + 1; } else { // We found a full MIME part! Parse it. part = new MimePart(); part.offset = start; part.length = lineStart - start; // Locate the headers (\r\n\r\n), searching from end of boundary line. for (start -= 2; start < (lineStart - 3); start++) { // Find the end of the headers if (Utility.CR == data[start] && Utility.CR == data[start + 2] && Utility.LF == data[start + 1] && Utility.LF == data[start + 3]) { // End of the headers found String head = Encoding.UTF8.GetString( data, part.offset, start - part.offset); String[] lines = head.Split(new String[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); // Parse the headers for this part. part.parseHeaders(lines); // End of headers reached, skip over the blank line start += 4; part.body = new byte[lineStart - start]; Array.Copy(data, start, part.body, 0, lineStart - start); // Done with headers break; } } if (null == part.headers) { // We never found the end of the headers. It's all body? part.body = new byte[part.length]; Array.Copy(data, part.offset, part.body, 0, part.length); } // All right, headers are parsed. Let's get the body now... else if (part.headers.ContainsKey("Content-Type")) { String ct = part.headers["Content-Type"]; // Find the offset of the value part of the character set. int cso = ct.IndexOf("charset"); if (-1 != cso) { // The charset field is present; get the body as a string. cso = ct.IndexOf('=', cso); String cs = ct.Substring(cso + 1).Trim(); Encoding enc = Encoding.GetEncoding(cs); part.bodyText = enc.GetString(part.body, 0, part.body.Length); } else if (!String.IsNullOrEmpty(part.multipartboundry)) { part.bodyParts = findParts(part.body, part.multipartboundry); } } else { // There are headers, but no Content-Type. // Probably an ordinary value; try to encode it as a string. try { part.bodyText = Encoding.UTF8.GetString( part.body, 0, part.body.Length); } catch (Exception) { } } // Part fully parsed parts.Add(part); // Keep going; there may be more parts. start = lineEnd + 1; } } } // Maybe the line wasn't long enough or it didn't have the boundary. // Or maybe it did, and it was the first boundary, so we have to find another. // Or maybe we even found a whole part, parsed it, and are now looking for more. lineStart = lineEnd; } // We didn't find an expected boundary so there's no (more) part(s). return(parts.Count > 0 ? parts.ToArray() : null); }
private byte[] parseRequest(byte[] request, int length, bool resume) { if (length > request.Length || length < 0) { throw new ArgumentOutOfRangeException("length", length, "The length must be between zero and request.Length, inclusive!"); } if (!resume) { // Start the search from the beginning current = 0; body = null; bodytext = null; bodyIndex = -1; } if (bodyIndex <= 0) { // Haven't found the end of the headers yet. for (; current < (length - 3); current++) { // Find the end of the headers if (Utility.CR == request[current] && Utility.CR == request[current + 2] && Utility.LF == request[current + 1] && Utility.LF == request[current + 3]) { // End of the headers found String head = Encoding.UTF8.GetString(request, 0, (int)current); String[] lines = head.Split(new String[] { "\r\n" }, StringSplitOptions.None); // Parse the first line parseFirstLine(lines[0]); // Parse the headers. This version doesn't mess up current parseHeaders(lines, 1); // End of headers reached, skip over the blank line current += 4; bodyIndex = current; break; } } } // Check whether the request is done if (-1 != bodyIndex && -1 == contentlength) { // We'd know if there's a body, and there isn't; we're done current = -1; if (length > bodyIndex) { // There may be another request past this one byte[] remainder = new byte[length - bodyIndex]; Array.Copy(request, (int)bodyIndex, remainder, 0, remainder.Length); return(remainder); } else { // No remainder in this particular packet return(null); } } else if ((contentlength > 0) && ((length - bodyIndex) >= contentlength)) { // We have the entire body already body = new byte[contentlength]; Array.Copy(request, bodyIndex, body, 0, (int)contentlength); if (headers.ContainsKey("Content-Type")) { String ct = headers["Content-Type"]; int csi = ct.IndexOf("charset"); if (-1 != csi) { // There is a character set for the body String cs = ct.Substring(ct.IndexOf('=', csi) + 1).Trim(); if (cs.Contains(';')) { cs = cs.Substring(0, cs.IndexOf(';')).Trim(); } Encoding enc = Encoding.GetEncoding(cs); bodytext = enc.GetString(body, 0, (int)contentlength); } else if (ct.Contains("text")) { // Text without specified charset. Oookay then, let's try autodetection. bodytext = new System.IO.StreamReader(new System.IO.MemoryStream(body)).ReadToEnd(); } else if (multipartboundry != null) { // Get the collection of parts. bodyparts = MimePart.findParts(body, multipartboundry); } } // Since we have the whole body... current = -1; long totallen = (contentlength > 0) ? bodyIndex + contentlength : bodyIndex; if (length > totallen) { // There may be another request past this one byte[] remainder = new byte[length - totallen]; Array.Copy(request, (int)totallen, remainder, 0, remainder.Length); return(remainder); } else { // No remainder in this particular packet return(null); } } else { // We don't have the full body yet return(request); } }