//http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4 //internal System.Collections.Specialized.NameValueCollection GetFormData() { internal System.Collections.Generic.List <Mime.MultipartPart> GetFormData() { System.Collections.Generic.List <Mime.MultipartPart> returnMultiPartData = new List <Mime.MultipartPart>(); if (this.RequestMethod != RequestMethods.POST || this.messageBody == null || this.messageBody.Length <= 0 || this.contentType == null) { return(returnMultiPartData); } else if (this.contentType.ToLower(System.Globalization.CultureInfo.InvariantCulture).StartsWith("application/x-www-form-urlencoded")) { Mime.MultipartPart mimeMultipart = new Mime.MultipartPart(GetUrlEncodedNameValueCollection(Utils.ByteConverter.ReadString(this.messageBody), true)); returnMultiPartData.Add(mimeMultipart); return(returnMultiPartData); } else if (this.contentType.ToLower(System.Globalization.CultureInfo.InvariantCulture).StartsWith("multipart/form-data")) { //http://www.ietf.org/rfc/rfc2388.txt //http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4 //wireshark: \epan\dissectors\packet-multipart.c //The content "multipart/form-data" follows the rules of all multipart MIME data streams as outlined in [RFC2045]. //see if there is a boundary in the Content-Type definition string contentTypeEnding = contentType.Substring(21); if (contentTypeEnding.StartsWith("boundary=")) { string boundary = contentTypeEnding.Substring(9); foreach (Mime.MultipartPart part in Mime.PartBuilder.GetParts(this.messageBody, boundary)) { returnMultiPartData.Add(part); } return(returnMultiPartData); } } return(null);//we failed to get the data }
private void parseMultipart(Mime.MultipartPart multipart, System.Collections.Specialized.NameValueCollection rootAttributes, Packets.TcpPacket tcpPacket, /*NetworkHost sourceHost, NetworkHost destinationHost, */ ref bool messageSentToPacketHandler, Encoding customEncoding, string from = null, string to = null, string subject = null, string messageId = null) { if (multipart.Attributes.Count > 0) { this.MainPacketHandler.OnParametersDetected(new PacketParser.Events.ParametersEventArgs(tcpPacket.ParentFrame.FrameNumber, this.fiveTuple, this.transferIsClientToServer, multipart.Attributes, tcpPacket.ParentFrame.Timestamp, this.protocol + " packet")); } string contentType = multipart.Attributes["Content-Type"]; string charset = multipart.Attributes["charset"]; if (charset != null && charset.Length > 0) { try { customEncoding = System.Text.Encoding.GetEncoding(charset); } catch { }; } bool attachment = false; string contentDisposition = multipart.Attributes["Content-Disposition"]; if (contentDisposition != null && contentDisposition.Contains("attachment")) { attachment = true; } if (contentType != null && ( contentType.Equals("multipart/mixed", StringComparison.InvariantCultureIgnoreCase) || contentType.Equals("multipart/alternative", StringComparison.InvariantCultureIgnoreCase) || contentType.Equals("multipart/related", StringComparison.InvariantCultureIgnoreCase) )) { /** * There are a variety of ways to attach images to an email. * Content types are used to identify what is contained by each part of the email. * As well as the various types of image, and text/plain and text/html for the text and HTML parts, * there are various containers: * multipart/alternative as a container for parts containing the same information in different formats, * multipart/related as a container for parts that are linked in some way, and * multipart/mixed as a general container. **/ //Mime.MultipartPart mimeMultipart = new Mime.MultipartPart(multipart.Data); System.IO.Stream mixedStream = new System.IO.MemoryStream(multipart.Data); Mime.UnbufferedReader mixedReader = new PacketParser.Mime.UnbufferedReader(mixedStream); string boundary = mixedReader.ReadLine(200, customEncoding); if (boundary != null && boundary.Length == 0)//there was an empty line before the boundary, try to read it again { boundary = mixedReader.ReadLine(200, customEncoding); } if (boundary != null && boundary.StartsWith("--")) { boundary = boundary.Substring(2); List <Mime.MultipartPart> innerParts = new List <Mime.MultipartPart>(Mime.PartBuilder.GetParts(mixedReader, boundary, customEncoding)); foreach (Mime.MultipartPart innerPart in innerParts) { //a bit of recursion here this.parseMultipart(innerPart, rootAttributes, tcpPacket, ref messageSentToPacketHandler, customEncoding, from, to, subject, messageId); } } } else if (!attachment && contentType == null || !attachment && contentType != null && (contentType.Equals("text/plain", StringComparison.InvariantCultureIgnoreCase) || !messageSentToPacketHandler && contentType.Equals("text/html", StringComparison.InvariantCultureIgnoreCase))) { //print the data as text //string textData = null; byte[] textDataBytes = null; if (multipart.Attributes["Content-Transfer-Encoding"] == "quoted-printable") { textDataBytes = Utils.ByteConverter.ReadQuotedPrintable(multipart.Data).ToArray(); //textData = Utils.ByteConverter.ReadString(); } else if (multipart.Attributes["Content-Transfer-Encoding"] == "base64") { textDataBytes = System.Convert.FromBase64String(Utils.ByteConverter.ReadString(multipart.Data)); //textData = Utils.ByteConverter.ReadString(); } else { textDataBytes = multipart.Data; //textData = Utils.ByteConverter.ReadString(); } string textData = null; if (customEncoding == null) { textData = Utils.ByteConverter.ReadString(textDataBytes); } else { textData = customEncoding.GetString(textDataBytes); } if (textData != null) { Dictionary <string, string> aggregatedAttributeDictionary = new Dictionary <string, string>(); System.Collections.Specialized.NameValueCollection aggregatedAttributes = new System.Collections.Specialized.NameValueCollection(); aggregatedAttributes.Add(rootAttributes); foreach (string name in rootAttributes.Keys) { aggregatedAttributeDictionary.Add(name, rootAttributes[name]); } foreach (string name in multipart.Attributes) { if (!aggregatedAttributeDictionary.ContainsKey(name)) { aggregatedAttributeDictionary.Add(name, multipart.Attributes[name]); aggregatedAttributes.Add(name, multipart.Attributes[name]); } } if (textData.Length > 0) { if (this.transferIsClientToServer) { this.MainPacketHandler.OnMessageDetected(new PacketParser.Events.MessageEventArgs(this.protocol, this.fiveTuple.ClientHost, this.fiveTuple.ServerHost, tcpPacket.ParentFrame.FrameNumber, tcpPacket.ParentFrame.Timestamp, from, to, subject, textData, customEncoding, aggregatedAttributes)); } else { this.MainPacketHandler.OnMessageDetected(new PacketParser.Events.MessageEventArgs(this.protocol, this.fiveTuple.ServerHost, this.fiveTuple.ClientHost, tcpPacket.ParentFrame.FrameNumber, tcpPacket.ParentFrame.Timestamp, from, to, subject, textData, customEncoding, aggregatedAttributes)); } } messageSentToPacketHandler = true; if (contentType != null && contentType.Equals("text/html", StringComparison.InvariantCultureIgnoreCase)) { //re-parse the multipart so that it is also extracted to an HTML file this.parseMultipart(multipart, rootAttributes, tcpPacket, ref messageSentToPacketHandler, customEncoding, from, to, subject, messageId); } } } else { //store the stuff to disk string filename = multipart.Attributes["name"]; if (filename == null || filename.Length == 0) { filename = multipart.Attributes["filename"]; } if (filename == null || filename.Length == 0) { if (subject != null && subject.Length > 3) { filename = Utils.StringManglerUtil.ConvertToFilename(subject, 10); } else if (messageId != null && messageId.Length > 3) { filename = Utils.StringManglerUtil.ConvertToFilename(messageId, 10); } if (filename == null || filename.Length < 3) { filename = "email_" + (multipart.GetHashCode() % 1000); } string extension = Utils.StringManglerUtil.GetExtension(contentType); if (extension == null || extension.Length < 1) { extension = "dat"; } filename = filename + "." + extension; } List <byte> fileData = new List <byte>(); if (multipart.Attributes["Content-Transfer-Encoding"] == "base64") { //decode base64 stuff int index = 0; while (index < multipart.Data.Length) { string base64 = Utils.ByteConverter.ReadLine(multipart.Data, ref index); if (base64 == null && index < multipart.Data.Length) { //read the remaining data base64 = Utils.ByteConverter.ReadString(multipart.Data, index, multipart.Data.Length - index, false, false); index = multipart.Data.Length; } #if DEBUG if (base64 == null) { System.Diagnostics.Debugger.Break(); } #endif //if (base64 != null && base64.Length > 0) { try { fileData.AddRange(Convert.FromBase64String(base64)); } catch (FormatException e) { } } } else if (multipart.Attributes["Content-Transfer-Encoding"] == "quoted-printable") { //must be decoded according to http://www.ietf.org/rfc/rfc2045.txt fileData = Utils.ByteConverter.ReadQuotedPrintable(multipart.Data); } else { //Add the raw data fileData.AddRange(multipart.Data); } if (fileData != null && fileData.Count > 0) { string fileId = GetFileId(rootAttributes); FileTransfer.FileStreamAssembler assembler = new FileTransfer.FileStreamAssembler(MainPacketHandler.FileStreamAssemblerList, this.fiveTuple, this.transferIsClientToServer, this.fileTransferProtocol, filename, "/", fileData.Count, fileData.Count, "E-mail From: " + from + " To: " + to + " Subject: " + subject, fileId, tcpPacket.ParentFrame.FrameNumber, tcpPacket.ParentFrame.Timestamp, this.fileAssmeblyRootLocation); if (assembler.TryActivate()) { assembler.FileReconstructed += MainPacketHandler.OnMessageAttachmentDetected; assembler.AddData(fileData.ToArray(), tcpPacket.SequenceNumber); //assembler.FinishAssembling(); } else { assembler.Clear(); assembler.FinishAssembling(); } } } }