/// <summary> /// Default constructor. /// </summary> /// <param name="mimeEntry"></param> /// <param name="mime"></param> public MimeEntry(byte[] mimeEntry, MimeParser mime) { MemoryStream entryStrm = new MemoryStream(mimeEntry); m_Headers = MimeParser.ParseHeaders(entryStrm); m_ContentType = mime.ParseContentType(m_Headers); m_Entries = new ArrayList(); // != multipart content if (m_ContentType.ToLower().IndexOf("multipart") == -1) { m_CharSet = ParseCharSet(m_Headers); m_ContentEncoding = ParseEncoding(m_Headers); m_Data = new byte[entryStrm.Length - entryStrm.Position]; entryStrm.Read(m_Data, 0, m_Data.Length); } // multipart content, get nested entries else { long s = (int)entryStrm.Position; string boundaryID = MimeParser.ParseHeaderFiledSubField("Content-Type:", "boundary", m_Headers); m_Entries = mime.ParseEntries(entryStrm, (int)entryStrm.Position, boundaryID); entryStrm.Position = s; m_Data = new byte[entryStrm.Length - s]; entryStrm.Read(m_Data, 0, m_Data.Length); } }
/// <summary> /// Default constructor. /// </summary> /// <param name="mimeEntry"></param> /// <param name="mime"></param> public MimeEntry(byte[] mimeEntry,MimeParser mime) { MemoryStream entryStrm = new MemoryStream(mimeEntry); m_Headers = MimeParser.ParseHeaders(entryStrm); m_ContentType = mime.ParseContentType(m_Headers); m_Entries = new ArrayList(); // != multipart content if(m_ContentType.ToLower().IndexOf("multipart") == -1){ m_CharSet = ParseCharSet(m_Headers); m_ContentEncoding = ParseEncoding(m_Headers); m_Data = new byte[entryStrm.Length - entryStrm.Position]; entryStrm.Read(m_Data,0,m_Data.Length); } // multipart content, get nested entries else{ long s = (int)entryStrm.Position; string boundaryID = MimeParser.ParseHeaderFiledSubField("Content-Type:","boundary",m_Headers); m_Entries = mime.ParseEntries(entryStrm,(int)entryStrm.Position,boundaryID); entryStrm.Position = s; m_Data = new byte[entryStrm.Length - s]; entryStrm.Read(m_Data,0,m_Data.Length); } }
/// <summary> /// Parse charset. /// </summary> /// <param name="headers"></param> /// <returns></returns> private string ParseCharSet(string headers) { string charset = MimeParser.ParseHeaderFiledSubField("Content-Type:", "charset", headers); // charset ends with ; remove it. Is it right place to do or can it be done MimeParser.ParseHeaderFiledSubField if (charset.EndsWith(";")) { charset = charset.Substring(0, charset.Length - 1); } if (charset.Length > 0) { try{ Encoding.GetEncoding(charset); return(charset); } catch { return("ascii"); } } // If no charset, consider it as ascii else { return("ascii"); } }
/// <summary> /// Parse encoding. quoted-printable,7bit,8bit,base64 is supported. /// </summary> /// <param name="headers"></param> /// <returns></returns> private string ParseEncoding(string headers) { string encoding = MimeParser.ParseHeaderField("CONTENT-TRANSFER-ENCODING:", headers); if (encoding.Length > 0) { return(encoding); } // If no encoding, consider it as ascii else { return("7bit"); } }
private Disposition ParseContentDisposition(string headers) { string disposition = MimeParser.ParseHeaderField("CONTENT-DISPOSITION:", headers); if (disposition.ToUpper().IndexOf("ATTACHMENT") > -1) { return(Disposition.Attachment); } if (disposition.ToUpper().IndexOf("INLINE") > -1) { return(Disposition.Inline); } return(Disposition.Unknown); }
/// <summary> /// Checks if message matches for specified search key. /// </summary> /// <param name="searchKey"></param> /// <param name="searchKeyValue"></param> /// <param name="messageInfo"></param> /// <param name="msg"></param> /// <returns></returns> public static bool MatchSearchKey(string searchKey,object searchKeyValue,IMAP_Message messageInfo,MimeParser msg) { // BEFORE <date> // Messages whose internal date (disregarding time and timezone) // is earlier than the specified date. if(searchKey == "BEFORE"){ if(messageInfo.Date.Date < (DateTime)searchKeyValue){ return true; } } // BODY <string> // Messages that contain the specified string in the body of the message. else if(searchKey == "BODY"){ if(msg.BodyText.IndexOf((string)searchKeyValue) > -1){ return true; } } // HEADER <field-name> <string> // Messages that have a header with the specified field-name (as // defined in [RFC-2822]) and that contains the specified string // in the text of the header (what comes after the colon). If the // string to search is zero-length, this matches all messages that // have a header line with the specified field-name regardless of // the contents. else if(searchKey == "HEADER"){ string[] headerField_value = (string[])searchKeyValue; string headerFileDValue = Core.CanonicalDecode(MimeParser.ParseHeaderField(headerField_value[0],msg.Headers)); if(headerField_value[1].Length == 0){ return true; } else if(headerFileDValue.IndexOf(headerField_value[1]) > -1){ return true; } } // KEYWORD <flag> // Messages with the specified keyword flag set. else if(searchKey == "KEYWORD"){ if((messageInfo.Flags & IMAP_Utils.ParseMessageFalgs((string)searchKeyValue)) != 0){ return true; } } // LARGER <n> // Messages with an [RFC-2822] size larger than the specified number of octets. else if(searchKey == "LARGER"){ if(messageInfo.Size > Convert.ToInt64(searchKeyValue)){ return true; } } // ON <date> // Messages whose internal date (disregarding time and timezone) // is within the specified date. else if(searchKey == "ON"){ if(messageInfo.Date.Date == (DateTime)searchKeyValue){ return true; } } // SENTBEFORE <date> // Messages whose [RFC-2822] Date: header (disregarding time and // timezone) is earlier than the specified date. else if(searchKey == "SENTBEFORE"){ if(msg.MessageDate.Date < (DateTime)searchKeyValue){ return true; } } // SENTON <date> // Messages whose [RFC-2822] Date: header (disregarding time and // timezone) is within the specified date. else if(searchKey == "SENTON"){ if(msg.MessageDate.Date == (DateTime)searchKeyValue){ return true; } } // SENTSINCE <date> // Messages whose [RFC-2822] Date: header (disregarding time and // timezone) is within or later than the specified date. else if(searchKey == "SENTSINCE"){ if(msg.MessageDate.Date >= (DateTime)searchKeyValue){ return true; } } // SINCE <date> // Messages whose internal date (disregarding time and timezone) // is within or later than the specified date. else if(searchKey == "SINCE"){ if(messageInfo.Date.Date >= (DateTime)searchKeyValue){ return true; } } // SMALLER <n> // Messages with an [RFC-2822] size smaller than the specified number of octets. else if(searchKey == "SMALLER"){ if(messageInfo.Size < Convert.ToInt64(searchKeyValue)){ return true; } } // TEXT <string> // Messages that contain the specified string in the header or body of the message. else if(searchKey == "TEXT"){ // TODO: } // UID <sequence set> // Messages with unique identifiers corresponding to the specified // unique identifier set. Sequence set ranges are permitted. else if(searchKey == "UID"){ if(((string)searchKeyValue).IndexOf(":") > -1){ string[] start_end = ((string)searchKeyValue).Split(':'); if(messageInfo.MessageUID >= Convert.ToInt64(start_end[0]) && messageInfo.MessageUID <= Convert.ToInt64(start_end[1])){ return true; } } else{ if(messageInfo.MessageUID == Convert.ToInt64(searchKeyValue)){ return true; } } } // UNKEYWORD <flag> // Messages that do not have the specified keyword flag set. else if(searchKey == "UNKEYWORD"){ if((messageInfo.Flags & IMAP_Utils.ParseMessageFalgs((string)searchKeyValue)) == 0){ return true; } } return false; }
private void Fetch(string cmdTag,string argsText,bool uidFetch) { /* Rfc 3501 6.4.5 FETCH Command Arguments: message set message data item names Responses: untagged responses: FETCH Result: OK - fetch completed NO - fetch error: can't fetch that data BAD - command unknown or arguments invalid The FETCH command retrieves data associated with a message in the mailbox. The data items to be fetched can be either a single atom or a parenthesized list. Most data items, identified in the formal syntax under the msg-att-static rule, are static and MUST NOT change for any particular message. Other data items, identified in the formal syntax under the msg-att-dynamic rule, MAY change, either as a result of a STORE command or due to external events. For example, if a client receives an ENVELOPE for a message when it already knows the envelope, it can safely ignore the newly transmitted envelope. There are three macros which specify commonly-used sets of data items, and can be used instead of data items. A macro must be used by itself, and not in conjunction with other macros or data items. ALL Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE) FAST Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE) FULL Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY) The currently defined data items that can be fetched are: BODY Non-extensible form of BODYSTRUCTURE. BODY[<section>]<<partial>> The text of a particular body section. The section specification is a set of zero or more part specifiers delimited by periods. A part specifier is either a part number or one of the following: HEADER, HEADER.FIELDS, HEADER.FIELDS.NOT, MIME, and TEXT. An empty section specification refers to the entire message, including the header. Every message has at least one part number. Non-[MIME-IMB] messages, and non-multipart [MIME-IMB] messages with no encapsulated message, only have a part 1. Multipart messages are assigned consecutive part numbers, as they occur in the message. If a particular part is of type message or multipart, its parts MUST be indicated by a period followed by the part number within that nested multipart part. A part of type MESSAGE/RFC822 also has nested part numbers, referring to parts of the MESSAGE part's body. The HEADER, HEADER.FIELDS, HEADER.FIELDS.NOT, and TEXT part specifiers can be the sole part specifier or can be prefixed by one or more numeric part specifiers, provided that the numeric part specifier refers to a part of type MESSAGE/RFC822. The MIME part specifier MUST be prefixed by one or more numeric part specifiers. The HEADER, HEADER.FIELDS, and HEADER.FIELDS.NOT part specifiers refer to the [RFC-2822] header of the message or of an encapsulated [MIME-IMT] MESSAGE/RFC822 message. HEADER.FIELDS and HEADER.FIELDS.NOT are followed by a list of field-name (as defined in [RFC-2822]) names, and return a subset of the header. The subset returned by HEADER.FIELDS contains only those header fields with a field-name that matches one of the names in the list; similarly, the subset returned by HEADER.FIELDS.NOT contains only the header fields with a non-matching field-name. The field-matching is case-insensitive but otherwise exact. Subsetting does not exclude the [RFC-2822] delimiting blank line between the header and the body; the blank line is included in all header fetches, except in the case of a message which has no body and no blank line. The MIME part specifier refers to the [MIME-IMB] header for this part. The TEXT part specifier refers to the text body of the message, omitting the [RFC-2822] header. Here is an example of a complex message with some of its part specifiers: HEADER ([RFC-2822] header of the message) TEXT ([RFC-2822] text body of the message) MULTIPART/MIXED 1 TEXT/PLAIN 2 APPLICATION/OCTET-STREAM 3 MESSAGE/RFC822 3.HEADER ([RFC-2822] header of the message) 3.TEXT ([RFC-2822] text body of the message) MULTIPART/MIXED 3.1 TEXT/PLAIN 3.2 APPLICATION/OCTET-STREAM 4 MULTIPART/MIXED 4.1 IMAGE/GIF 4.1.MIME ([MIME-IMB] header for the IMAGE/GIF) 4.2 MESSAGE/RFC822 4.2.HEADER ([RFC-2822] header of the message) 4.2.TEXT ([RFC-2822] text body of the message) MULTIPART/MIXED 4.2.1 TEXT/PLAIN 4.2.2 MULTIPART/ALTERNATIVE 4.2.2.1 TEXT/PLAIN 4.2.2.2 TEXT/RICHTEXT It is possible to fetch a substring of the designated text. This is done by appending an open angle bracket ("<"), the octet position of the first desired octet, a period, the maximum number of octets desired, and a close angle bracket (">") to the part specifier. If the starting octet is beyond the end of the text, an empty string is returned. Any partial fetch that attempts to read beyond the end of the text is truncated as appropriate. A partial fetch that starts at octet 0 is returned as a partial fetch, even if this truncation happened. Note: This means that BODY[]<0.2048> of a 1500-octet message will return BODY[]<0> with a literal of size 1500, not BODY[]. Note: A substring fetch of a HEADER.FIELDS or HEADER.FIELDS.NOT part specifier is calculated after subsetting the header. The \Seen flag is implicitly set; if this causes the flags to change, they SHOULD be included as part of the FETCH responses. BODY.PEEK[<section>]<<partial>> An alternate form of BODY[<section>] that does not implicitly set the \Seen flag. BODYSTRUCTURE The [MIME-IMB] body structure of the message. This is computed by the server by parsing the [MIME-IMB] header fields in the [RFC-2822] header and [MIME-IMB] headers. ENVELOPE The envelope structure of the message. This is computed by the server by parsing the [RFC-2822] header into the component parts, defaulting various fields as necessary. FLAGS The flags that are set for this message. INTERNALDATE The internal date of the message. RFC822 Functionally equivalent to BODY[], differing in the syntax of the resulting untagged FETCH data (RFC822 is returned). RFC822.HEADER Functionally equivalent to BODY.PEEK[HEADER], differing in the syntax of the resulting untagged FETCH data (RFC822.HEADER is returned). RFC822.SIZE The [RFC-2822] size of the message. RFC822.TEXT Functionally equivalent to BODY[TEXT], differing in the syntax of the resulting untagged FETCH data (RFC822.TEXT is returned). UID The unique identifier for the message. Example: C: A654 FETCH 2:4 (FLAGS BODY[HEADER.FIELDS (DATE FROM)]) S: * 2 FETCH .... S: * 3 FETCH .... S: * 4 FETCH .... S: A654 OK FETCH completed */ if(!m_Authenticated){ m_pSocket.SendLine(cmdTag + " NO Authenticate first !"); return; } if(m_SelectedMailbox.Length == 0){ m_pSocket.SendLine(cmdTag + " NO Select mailbox first !"); return; } #region Parse parameters string[] args = ParseParams(argsText); if(args.Length != 2){ m_pSocket.SendLine(cmdTag + " BAD Invalid arguments"); return; } ArrayList seq_set = ParseMsgNumbersFromSequenceSet(args[0].Trim(),uidFetch); // Replace macros string fetchItems = args[1].ToUpper(); fetchItems = fetchItems.Replace("ALL" ,"FLAGS INTERNALDATE RFC822.SIZE ENVELOPE"); fetchItems = fetchItems.Replace("FAST","FLAGS INTERNALDATE RFC822.SIZE"); fetchItems = fetchItems.Replace("FULL","FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY"); // If UID FETCH and no UID, we must implicity add it, it's required if(uidFetch && fetchItems.ToUpper().IndexOf("UID") == -1){ fetchItems += " UID"; } // ToDo: ??? start parm parsing from left to end in while loop while params parsed or bad param found bool headersNeeded = false; bool fullMsgNeeded = false; // Parse,validate requested fetch items Hashtable items = new Hashtable(); if(fetchItems.IndexOf("UID") > -1){ items.Add("UID",""); fetchItems = fetchItems.Replace("UID",""); } if(fetchItems.IndexOf("RFC822.TEXT") > -1){ fullMsgNeeded = true; items.Add("RFC822.TEXT",""); fetchItems = fetchItems.Replace("RFC822.TEXT",""); } if(fetchItems.IndexOf("RFC822.SIZE") > -1){ items.Add("RFC822.SIZE",""); fetchItems = fetchItems.Replace("RFC822.SIZE",""); } if(fetchItems.IndexOf("RFC822.HEADER") > -1){ headersNeeded = true; items.Add("RFC822.HEADER",""); fetchItems = fetchItems.Replace("RFC822.HEADER",""); } if(fetchItems.IndexOf("RFC822") > -1){ fullMsgNeeded = true; items.Add("RFC822",""); fetchItems = fetchItems.Replace("RFC822",""); } if(fetchItems.IndexOf("INTERNALDATE") > -1){ items.Add("INTERNALDATE",""); fetchItems = fetchItems.Replace("INTERNALDATE",""); } if(fetchItems.IndexOf("FLAGS") > -1){ items.Add("FLAGS",""); fetchItems = fetchItems.Replace("FLAGS",""); } if(fetchItems.IndexOf("ENVELOPE") > -1){ headersNeeded = true; items.Add("ENVELOPE",""); fetchItems = fetchItems.Replace("ENVELOPE",""); } if(fetchItems.IndexOf("BODYSTRUCTURE") > -1){ fullMsgNeeded = true; items.Add("BODYSTRUCTURE",""); fetchItems = fetchItems.Replace("BODYSTRUCTURE",""); } if(fetchItems.IndexOf("BODY.PEEK[") > -1){ int start = fetchItems.IndexOf("BODY.PEEK[") + 10; string val = fetchItems.Substring(start,fetchItems.IndexOf("]",start) - start).ToUpper().Trim(); // Remove BODY.PEEK[...] from remaining args string. fetchItems = fetchItems.Substring(fetchItems.IndexOf("]") + 1); //--- See if partial fetch. For example: BODY.PEEK[]<0.100> long startPosition = 0; long maxLength = long.MaxValue; bool partial = false; if(fetchItems.StartsWith("<")){ // We got partial fetch, need to get data between <> string partialArgs = fetchItems.Substring(1,fetchItems.IndexOf(">") - 1); string[] pArgs = partialArgs.Split('.'); startPosition = Convert.ToInt64(pArgs[0]); maxLength = Convert.ToInt64(pArgs[1]); // Remove partial fetch args(<...>) from remaining args string. fetchItems = fetchItems.Substring(fetchItems.IndexOf(">") + 1); partial = true; } //---------------------------------------------------------------- // We must support only: // "" - full message // TEXT - message text // HEADER - message header // HEADER.FIELDS - requested message header fields // HEADER.FIELDS.NOT - message header fields except requested // number of mime entry - Example: BODY[1];BODY[1.1];BODY[1.1.x. ...] if(val.Length > 0){ string[] fArgs = ParseParams(val); // Specified number of mime entry requested // if(fArgs.Length == 1 && Core.IsNumber(fArgs[0])){ // fullMsgNeeded = true; // items.Add("BODY.PEEK[NUMBER]",new object[]{partial,startPosition,maxLength,Convert.ToInt32(fArgs[0])}); // } // else{ switch(fArgs[0].ToUpper()) { case "TEXT": fullMsgNeeded = true; items.Add("BODY.PEEK[TEXT]",new object[]{partial,startPosition,maxLength}); break; case "HEADER": headersNeeded = true; items.Add("BODY.PEEK[HEADER]",new object[]{partial,startPosition,maxLength}); break; case "HEADER.FIELDS": if(fArgs.Length == 2){ headersNeeded = true; items.Add("BODY.PEEK[HEADER.FIELDS]",new object[]{partial,startPosition,maxLength,fArgs[1]}); } break; case "HEADER.FIELDS.NOT": if(fArgs.Length == 2){ headersNeeded = true; items.Add("BODY.PEEK[HEADER.FIELDS.NOT]",new object[]{partial,startPosition,maxLength,fArgs[1]}); } break; default: // This must be number of mime entry fullMsgNeeded = true; items.Add("BODY.PEEK[NUMBER]",new object[]{partial,startPosition,maxLength,fArgs[0]}); break; // m_pSocket.SendLine(cmdTag + " BAD Invalid fetch-items argument"); // return; } // } } else{ fullMsgNeeded = true; items.Add("BODY.PEEK[]",new object[]{partial,startPosition,maxLength}); } } // BODY[<section>]<<partial>> if(fetchItems.IndexOf("BODY[") > -1){ int start = fetchItems.IndexOf("BODY[") + 5; string val = fetchItems.Substring(start,fetchItems.IndexOf("]",start) - start).ToUpper().Trim(); // Remove BODY[...] from remaining args string. fetchItems = fetchItems.Substring(fetchItems.IndexOf("]") + 1); //--- See if partial fetch. For example: BODY[]<0.100> long startPosition = 0; long maxLength = long.MaxValue; bool partial = false; if(fetchItems.StartsWith("<")){ // We got partial fetch, need to get data between <> string partialArgs = fetchItems.Substring(1,fetchItems.IndexOf(">") - 1); string[] pArgs = partialArgs.Split('.'); startPosition = Convert.ToInt64(pArgs[0]); maxLength = Convert.ToInt64(pArgs[1]); // Remove partial fetch args(<...>) from remaining args string. fetchItems = fetchItems.Substring(fetchItems.IndexOf(">") + 1); partial = true; } //---------------------------------------------------------------- // We must support only: // "" - full message // TEXT - message text // HEADER - message header // HEADER.FIELDS - requested message header fields // HEADER.FIELDS.NOT - message header fields except requested // number of mime entry - Example: BODY[1];BODY[1.1];BODY[1.1.x. ...] if(val.Length > 0){ string[] fArgs = ParseParams(val); // Specified number of mime entry requested // if(fArgs.Length == 1 && Core.IsNumber(fArgs[0])){ // fullMsgNeeded = true; // items.Add("BODY[NUMBER]",new object[]{partial,startPosition,maxLength,Convert.ToInt32(fArgs[0])}); // } // else{ switch(fArgs[0].ToUpper()) { case "TEXT": fullMsgNeeded = true; items.Add("BODY[TEXT]",new object[]{partial,startPosition,maxLength}); break; case "HEADER": headersNeeded = true; items.Add("BODY[HEADER]",new object[]{partial,startPosition,maxLength}); break; case "HEADER.FIELDS": if(fArgs.Length == 2){ headersNeeded = true; items.Add("BODY[HEADER.FIELDS]",new object[]{partial,startPosition,maxLength,fArgs[1]}); } break; case "HEADER.FIELDS.NOT": if(fArgs.Length == 2){ headersNeeded = true; items.Add("BODY[HEADER.FIELDS.NOT]",new object[]{partial,startPosition,maxLength,fArgs[1]}); } break; default: // This must be number of mime entry fullMsgNeeded = true; items.Add("BODY[NUMBER]",new object[]{partial,startPosition,maxLength,fArgs[0]}); break; // m_pSocket.SendLine(cmdTag + " BAD Invalid fetch-items argument"); // return; } // } } else{ fullMsgNeeded = true; items.Add("BODY[]",new object[]{partial,startPosition,maxLength}); } } if(fetchItems.IndexOf("BODY") > -1){ fullMsgNeeded = true; items.Add("BODY",""); fetchItems = fetchItems.Replace("BODY",""); } // If length != 0, then contains invalid fetch items if(fetchItems.Trim().Length > 0){ m_pSocket.SendLine(cmdTag + " BAD Invalid fetch-items argument"); return; } #endregion // ToDo: // The server should respond with a tagged BAD response to a command that uses a message // sequence number greater than the number of messages in the selected mailbox. This // includes "*" if the selected mailbox is empty. // if(m_Messages.Count == 0 || ){ // SendData(cmdTag + " BAD Sequence number greater than the number of messages in the selected mailbox !\r\n"); // return; // } // ToDo: Move to all parts to MimeParse where possible, this avoid multiple decodings for(int i=0;i<m_Messages.Count;i++){ // if(seq_set.Contains(i + 1)){ IMAP_Message msg = m_Messages[i]; byte[] buf = null; MemoryStream reply = new MemoryStream(); // Write fetch start data "* msgNo FETCH (" buf = Encoding.ASCII.GetBytes("* " + msg.MessageNo + " FETCH ("); reply.Write(buf,0,buf.Length); byte[] msgData = null; byte[] msgHeadData = null; MimeParser parser = null; // Check if header or data is neccessary. Eg. if only UID wanted, don't get message at all. if(fullMsgNeeded || headersNeeded){ Message_EventArgs eArgs = m_pServer.OnGetMessage(this,msg,!fullMsgNeeded); msgData = eArgs.MessageData; // Headers needed parse headers from msgData if(headersNeeded){ string headers = InfoControl.Net.Mail.Mime.MimeParser.ParseHeaders(new MemoryStream(msgData)); msgHeadData = Encoding.Default.GetBytes(headers + "\r\n"); // blank line is included in all header fetches } parser = new MimeParser(msgData); } IMAP_MessageFlags msgFlagsOr = msg.Flags; // Construct reply here, based on requested fetch items int nCount = 0; foreach(string fetchItem in items.Keys){ object[] partArgs = null; bool partial = false; long startPosition = 0; long maxLength = 0; long lengthToSend = 0; string partNumber = ""; string headerFields = ""; switch(fetchItem) { case "UID": buf = Encoding.ASCII.GetBytes("UID " + msg.MessageUID); reply.Write(buf,0,buf.Length); break; case "RFC822.TEXT": // Sets \seen flag msg.SetFlags(msg.Flags | IMAP_MessageFlags.Seen); // RFC822.TEXT {size} // msg text byte[] f11Data = System.Text.Encoding.ASCII.GetBytes(parser.BodyText); buf = Encoding.ASCII.GetBytes("RFC822.TEXT {" + f11Data.Length + "}\r\n"); reply.Write(buf,0,buf.Length); reply.Write(f11Data,0,f11Data.Length); break; case "RFC822.SIZE": // "RFC822.SIZE size buf = Encoding.ASCII.GetBytes("RFC822.SIZE " + msg.Size); reply.Write(buf,0,buf.Length); break; case "RFC822.HEADER": // RFC822.HEADER {size} // msg header data buf = Encoding.ASCII.GetBytes("RFC822.HEADER {" + msgHeadData.Length + "}\r\n"); reply.Write(buf,0,buf.Length); reply.Write(msgHeadData,0,msgHeadData.Length); break; case "RFC822": // Sets \seen flag msg.SetFlags(msg.Flags | IMAP_MessageFlags.Seen); // RFC822 {size} // msg data buf = Encoding.ASCII.GetBytes("RFC822 {" + msgData.Length + "}\r\n"); reply.Write(buf,0,buf.Length); reply.Write(msgData,0,msgData.Length); break; case "INTERNALDATE": // INTERNALDATE "date" buf = Encoding.ASCII.GetBytes("INTERNALDATE \"" + msg.Date.ToString("r",System.Globalization.DateTimeFormatInfo.InvariantInfo) + "\""); reply.Write(buf,0,buf.Length); break; case "FLAGS": buf = Encoding.ASCII.GetBytes("FLAGS (" + msg.FlagsToString() + ")"); reply.Write(buf,0,buf.Length); break; case "ENVELOPE": buf = Encoding.ASCII.GetBytes(FetchHelper.ConstructEnvelope(parser)); reply.Write(buf,0,buf.Length); break; case "BODYSTRUCTURE": // BODYSTRUCTURE () buf = Encoding.ASCII.GetBytes(FetchHelper.ConstructBodyStructure(parser,true)); reply.Write(buf,0,buf.Length); break; case "BODY.PEEK[]": // BODY[] {size} // msg header data partArgs = (object[])items[fetchItem]; partial = (bool)partArgs[0]; startPosition = (long)partArgs[1]; maxLength = (long)partArgs[2]; lengthToSend = msgData.Length - startPosition; if(lengthToSend > maxLength){ lengthToSend = maxLength; } if(lengthToSend < 0){ lengthToSend = 0; } if(partial){ buf = Encoding.ASCII.GetBytes("BODY[]<" + startPosition + "> {" + lengthToSend + "}\r\n"); } else{ buf = Encoding.ASCII.GetBytes("BODY[] {" + lengthToSend + "}\r\n"); } reply.Write(buf,0,buf.Length); reply.Write(msgData,(int)startPosition,(int)lengthToSend); break; case "BODY.PEEK[HEADER]": // BODY[HEADER] {size} // msg header data partArgs = (object[])items[fetchItem]; partial = (bool)partArgs[0]; startPosition = (long)partArgs[1]; maxLength = (long)partArgs[2]; lengthToSend = msgHeadData.Length - startPosition; if(lengthToSend > maxLength){ lengthToSend = maxLength; } if(lengthToSend < 0){ lengthToSend = 0; } if(partial){ buf = Encoding.ASCII.GetBytes("BODY[HEADER]<" + startPosition + "> {" + lengthToSend + "}\r\n"); } else{ buf = Encoding.ASCII.GetBytes("BODY[HEADER] {" + lengthToSend + "}\r\n"); } reply.Write(buf,0,buf.Length); reply.Write(msgHeadData,(int)startPosition,(int)lengthToSend); break; case "BODY.PEEK[HEADER.FIELDS]": // BODY[HEADER.FIELDS ()] {size} // msg header data partArgs = (object[])items[fetchItem]; partial = (bool)partArgs[0]; startPosition = (long)partArgs[1]; maxLength = (long)partArgs[2]; headerFields = (string)partArgs[3]; byte[] fData = Encoding.ASCII.GetBytes(FetchHelper.ParseHeaderFields(headerFields,msgHeadData)); lengthToSend = fData.Length - startPosition; if(lengthToSend > maxLength){ lengthToSend = maxLength; } if(lengthToSend < 0){ lengthToSend = 0; } if(partial){ buf = Encoding.ASCII.GetBytes("BODY[HEADER.FIELDS (" + headerFields + ")]<" + startPosition + "> {" + lengthToSend + "}\r\n"); } else{ buf = Encoding.ASCII.GetBytes("BODY[HEADER.FIELDS (" + headerFields + ")] {" + lengthToSend + "}\r\n"); } reply.Write(buf,0,buf.Length); reply.Write(fData,(int)startPosition,(int)lengthToSend); break; case "BODY.PEEK[HEADER.FIELDS.NOT]": // BODY[HEADER.FIELDS.NOT ()] {size} // msg header data partArgs = (object[])items[fetchItem]; partial = (bool)partArgs[0]; startPosition = (long)partArgs[1]; maxLength = (long)partArgs[2]; headerFields = (string)partArgs[3]; byte[] f1Data = Encoding.ASCII.GetBytes(FetchHelper.ParseHeaderFieldsNot(headerFields,msgHeadData)); lengthToSend = f1Data.Length - startPosition; if(lengthToSend > maxLength){ lengthToSend = maxLength; } if(lengthToSend < 0){ lengthToSend = 0; } if(partial){ buf = Encoding.ASCII.GetBytes("BODY[HEADER.FIELDS.NOT (" + headerFields + ")]<" + startPosition + "> {" + lengthToSend + "}\r\n"); } else{ buf = Encoding.ASCII.GetBytes("BODY[HEADER.FIELDS.NOT (" + headerFields + ")] {" + lengthToSend + "}\r\n"); } reply.Write(buf,0,buf.Length); reply.Write(f1Data,(int)startPosition,(int)lengthToSend); break; case "BODY.PEEK[TEXT]": // BODY[TEXT] {size} // msg text partArgs = (object[])items[fetchItem]; partial = (bool)partArgs[0]; startPosition = (long)partArgs[1]; maxLength = (long)partArgs[2]; byte[] f111Data = Encoding.ASCII.GetBytes(parser.BodyText); lengthToSend = f111Data.Length - startPosition; if(lengthToSend > maxLength){ lengthToSend = maxLength; } if(lengthToSend < 0){ lengthToSend = 0; } if(partial){ buf = Encoding.ASCII.GetBytes("BODY[TEXT]<" + startPosition + "> {" + lengthToSend + "}\r\n"); } else{ buf = Encoding.ASCII.GetBytes("BODY[TEXT] {" + lengthToSend + "}\r\n"); } reply.Write(buf,0,buf.Length); reply.Write(f111Data,(int)startPosition,(int)lengthToSend); break; case "BODY.PEEK[NUMBER]": // BODY[no.] {size} // mime part partArgs = (object[])items[fetchItem]; partial = (bool)partArgs[0]; startPosition = (long)partArgs[1]; maxLength = (long)partArgs[2]; partNumber = partArgs[3].ToString(); byte[] b1113Data = FetchHelper.ParseMimeEntry(parser,partNumber); if(b1113Data != null){ lengthToSend = b1113Data.Length - startPosition; if(lengthToSend > maxLength){ lengthToSend = maxLength; } if(lengthToSend < 0){ lengthToSend = 0; } if(partial){ buf = Encoding.ASCII.GetBytes("BODY[" + partNumber + "]<" + startPosition + "> {" + lengthToSend + "}\r\n"); } else{ buf = Encoding.ASCII.GetBytes("BODY[" + partNumber + "] {" + lengthToSend + "}\r\n"); } reply.Write(buf,0,buf.Length); reply.Write(b1113Data,(int)startPosition,(int)lengthToSend); } else{ // BODY[no.] NIL buf = Encoding.ASCII.GetBytes("BODY[" + partNumber + "] NIL"); reply.Write(buf,0,buf.Length); } break; case "BODY[]": // Sets \seen flag msg.SetFlags(msg.Flags | IMAP_MessageFlags.Seen); // BODY[] {size} // msg header data partArgs = (object[])items[fetchItem]; partial = (bool)partArgs[0]; startPosition = (long)partArgs[1]; maxLength = (long)partArgs[2]; lengthToSend = msgData.Length - startPosition; if(lengthToSend > maxLength){ lengthToSend = maxLength; } if(lengthToSend < 0){ lengthToSend = 0; } if(partial){ buf = Encoding.ASCII.GetBytes("BODY[]<" + startPosition + "> {" + lengthToSend + "}\r\n"); } else{ buf = Encoding.ASCII.GetBytes("BODY[] {" + lengthToSend + "}\r\n"); } reply.Write(buf,0,buf.Length); reply.Write(msgData,(int)startPosition,(int)lengthToSend); break; case "BODY[HEADER]": // Sets \seen flag msg.SetFlags(msg.Flags | IMAP_MessageFlags.Seen); // BODY[HEADER] {size} // msg header data partArgs = (object[])items[fetchItem]; partial = (bool)partArgs[0]; startPosition = (long)partArgs[1]; maxLength = (long)partArgs[2]; lengthToSend = msgHeadData.Length - startPosition; if(lengthToSend > maxLength){ lengthToSend = maxLength; } if(lengthToSend < 0){ lengthToSend = 0; } if(partial){ buf = Encoding.ASCII.GetBytes("BODY[HEADER]<" + startPosition + "> {" + lengthToSend + "}\r\n"); } else{ buf = Encoding.ASCII.GetBytes("BODY[HEADER] {" + lengthToSend + "}\r\n"); } reply.Write(buf,0,buf.Length); reply.Write(msgHeadData,(int)startPosition,(int)lengthToSend); break; case "BODY[HEADER.FIELDS]": // Sets \seen flag msg.SetFlags(msg.Flags | IMAP_MessageFlags.Seen); // BODY[HEADER.FIELDS ()] {size} // msg header data partArgs = (object[])items[fetchItem]; partial = (bool)partArgs[0]; startPosition = (long)partArgs[1]; maxLength = (long)partArgs[2]; headerFields = (string)partArgs[3]; byte[] bData = Encoding.ASCII.GetBytes(FetchHelper.ParseHeaderFields(headerFields,msgHeadData)); lengthToSend = bData.Length - startPosition; if(lengthToSend > maxLength){ lengthToSend = maxLength; } if(lengthToSend < 0){ lengthToSend = 0; } if(partial){ buf = Encoding.ASCII.GetBytes("BODY[HEADER.FIELDS (" + headerFields + ")]<" + startPosition + "> {" + lengthToSend + "}\r\n"); } else{ buf = Encoding.ASCII.GetBytes("BODY[HEADER.FIELDS (" + headerFields + ")] {" + lengthToSend + "}\r\n"); } reply.Write(buf,0,buf.Length); reply.Write(bData,(int)startPosition,(int)lengthToSend); break; case "BODY[HEADER.FIELDS.NOT]": // Sets \seen flag msg.SetFlags(msg.Flags | IMAP_MessageFlags.Seen); // BODY[HEADER.FIELDS.NOT ()] {size} // msg header data partArgs = (object[])items[fetchItem]; partial = (bool)partArgs[0]; startPosition = (long)partArgs[1]; maxLength = (long)partArgs[2]; headerFields = (string)partArgs[3]; byte[] f2Data = Encoding.ASCII.GetBytes(FetchHelper.ParseHeaderFieldsNot(headerFields,msgHeadData)); lengthToSend = f2Data.Length - startPosition; if(lengthToSend > maxLength){ lengthToSend = maxLength; } if(lengthToSend < 0){ lengthToSend = 0; } if(partial){ buf = Encoding.ASCII.GetBytes("BODY[HEADER.FIELDS.NOT (" + headerFields + ")]<" + startPosition + "> {" + lengthToSend + "}\r\n"); } else{ buf = Encoding.ASCII.GetBytes("BODY[HEADER.FIELDS.NOT (" + headerFields + ")] {" + lengthToSend + "}\r\n"); } reply.Write(buf,0,buf.Length); reply.Write(f2Data,(int)startPosition,(int)lengthToSend); break; case "BODY[TEXT]": // Sets \seen flag msg.SetFlags(msg.Flags | IMAP_MessageFlags.Seen); // BODY[TEXT] {size} // msg text partArgs = (object[])items[fetchItem]; partial = (bool)partArgs[0]; startPosition = (long)partArgs[1]; maxLength = (long)partArgs[2]; byte[] f1111Data = Encoding.ASCII.GetBytes(parser.BodyText); lengthToSend = f1111Data.Length - startPosition; if(lengthToSend > maxLength){ lengthToSend = maxLength; } if(partial){ buf = Encoding.ASCII.GetBytes("BODY[TEXT]<" + startPosition + "> {" + lengthToSend + "}\r\n"); } else{ buf = Encoding.ASCII.GetBytes("BODY[TEXT] {" + lengthToSend + "}\r\n"); } reply.Write(buf,0,buf.Length); reply.Write(f1111Data,(int)startPosition,(int)lengthToSend); break; case "BODY[NUMBER]": // Sets \seen flag msg.SetFlags(msg.Flags | IMAP_MessageFlags.Seen); // BODY[no.] {size} // mime part partArgs = (object[])items[fetchItem]; partial = (bool)partArgs[0]; startPosition = (long)partArgs[1]; maxLength = (long)partArgs[2]; partNumber = partArgs[3].ToString(); byte[] b113Data = FetchHelper.ParseMimeEntry(parser,partNumber); if(b113Data != null){ lengthToSend = b113Data.Length - startPosition; if(lengthToSend > maxLength){ lengthToSend = maxLength; } if(lengthToSend < 0){ lengthToSend = 0; } if(partial){ buf = Encoding.ASCII.GetBytes("BODY[" + partNumber + "]<" + startPosition + "> {" + lengthToSend + "}\r\n"); } else{ buf = Encoding.ASCII.GetBytes("BODY[" + partNumber + "] {" + lengthToSend + "}\r\n"); } reply.Write(buf,0,buf.Length); reply.Write(b113Data,(int)startPosition,(int)lengthToSend); } else{ // BODY[no.] NIL buf = Encoding.ASCII.GetBytes("BODY[" + partNumber + "] NIL"); reply.Write(buf,0,buf.Length); } break; case "BODY": // Sets \seen flag // BODY () buf = Encoding.ASCII.GetBytes(FetchHelper.ConstructBodyStructure(parser,false)); reply.Write(buf,0,buf.Length); break; } nCount++; // Write fetch item separator data " " // We don't write it for last item if(nCount < items.Count){ buf = Encoding.ASCII.GetBytes(" "); reply.Write(buf,0,buf.Length); } } // Write fetch end data ")" buf = Encoding.ASCII.GetBytes(")\r\n"); reply.Write(buf,0,buf.Length); // Send fetch reply to client reply.Position = 0; m_pSocket.SendData(reply); // Set message flags here if required or changed if(((int)IMAP_MessageFlags.Recent & (int)msg.Flags) != 0 || msgFlagsOr != msg.Flags){ msg.SetFlags(msg.Flags & ~IMAP_MessageFlags.Recent); m_pServer.OnStoreMessageFlags(this,msg); } } } m_pSocket.SendLine(cmdTag + " OK FETCH completed"); }
// #region function Search private void Search(string cmdTag,string argsText,bool uidSearch) { /* Rfc 3501 6.4.4 SEARCH Command Arguments: OPTIONAL [CHARSET] specification searching criteria (one or more) Responses: REQUIRED untagged response: SEARCH Result: OK - search completed NO - search error: can't search that [CHARSET] or criteria BAD - command unknown or arguments invalid The SEARCH command searches the mailbox for messages that match the given searching criteria. Searching criteria consist of one or more search keys. The untagged SEARCH response from the server contains a listing of message sequence numbers corresponding to those messages that match the searching criteria. When multiple keys are specified, the result is the intersection (AND function) of all the messages that match those keys. For example, the criteria DELETED FROM "SMITH" SINCE 1-Feb-1994 refers to all deleted messages from Smith that were placed in the mailbox since February 1, 1994. A search key can also be a parenthesized list of one or more search keys (e.g., for use with the OR and NOT keys). Server implementations MAY exclude [MIME-IMB] body parts with terminal content media types other than TEXT and MESSAGE from consideration in SEARCH matching. The OPTIONAL [CHARSET] specification consists of the word "CHARSET" followed by a registered [CHARSET]. It indicates the [CHARSET] of the strings that appear in the search criteria. [MIME-IMB] content transfer encodings, and [MIME-HDRS] strings in [RFC-2822]/[MIME-IMB] headers, MUST be decoded before comparing text in a [CHARSET] other than US-ASCII. US-ASCII MUST be supported; other [CHARSET]s MAY be supported. If the server does not support the specified [CHARSET], it MUST return a tagged NO response (not a BAD). This response SHOULD contain the BADCHARSET response code, which MAY list the [CHARSET]s supported by the server. In all search keys that use strings, a message matches the key if the string is a substring of the field. The matching is case-insensitive. The defined search keys are as follows. Refer to the Formal Syntax section for the precise syntactic definitions of the arguments. <sequence set> Messages with message sequence numbers corresponding to the specified message sequence number set. ALL All messages in the mailbox; the default initial key for ANDing. ANSWERED Messages with the \Answered flag set. BCC <string> Messages that contain the specified string in the envelope structure's BCC field. BEFORE <date> Messages whose internal date (disregarding time and timezone) is earlier than the specified date. BODY <string> Messages that contain the specified string in the body of the message. CC <string> Messages that contain the specified string in the envelope structure's CC field. DELETED Messages with the \Deleted flag set. DRAFT Messages with the \Draft flag set. FLAGGED Messages with the \Flagged flag set. FROM <string> Messages that contain the specified string in the envelope structure's FROM field. HEADER <field-name> <string> Messages that have a header with the specified field-name (as defined in [RFC-2822]) and that contains the specified string in the text of the header (what comes after the colon). If the string to search is zero-length, this matches all messages that have a header line with the specified field-name regardless of the contents. KEYWORD <flag> Messages with the specified keyword flag set. LARGER <n> Messages with an [RFC-2822] size larger than the specified number of octets. NEW Messages that have the \Recent flag set but not the \Seen flag. This is functionally equivalent to "(RECENT UNSEEN)". NOT <search-key> Messages that do not match the specified search key. OLD Messages that do not have the \Recent flag set. This is functionally equivalent to "NOT RECENT" (as opposed to "NOT NEW"). ON <date> Messages whose internal date (disregarding time and timezone) is within the specified date. OR <search-key1> <search-key2> Messages that match either search key. RECENT Messages that have the \Recent flag set. SEEN Messages that have the \Seen flag set. SENTBEFORE <date> Messages whose [RFC-2822] Date: header (disregarding time and timezone) is earlier than the specified date. SENTON <date> Messages whose [RFC-2822] Date: header (disregarding time and timezone) is within the specified date. SENTSINCE <date> Messages whose [RFC-2822] Date: header (disregarding time and timezone) is within or later than the specified date. SINCE <date> Messages whose internal date (disregarding time and timezone) is within or later than the specified date. SMALLER <n> Messages with an [RFC-2822] size smaller than the specified number of octets. SUBJECT <string> Messages that contain the specified string in the envelope structure's SUBJECT field. TEXT <string> Messages that contain the specified string in the header or body of the message. TO <string> Messages that contain the specified string in the envelope structure's TO field. UID <sequence set> Messages with unique identifiers corresponding to the specified unique identifier set. Sequence set ranges are permitted. UNANSWERED Messages that do not have the \Answered flag set. UNDELETED Messages that do not have the \Deleted flag set. UNDRAFT Messages that do not have the \Draft flag set. UNFLAGGED Messages that do not have the \Flagged flag set. UNKEYWORD <flag> Messages that do not have the specified keyword flag set. UNSEEN Messages that do not have the \Seen flag set. Example: C: A282 SEARCH FLAGGED SINCE 1-Feb-1994 NOT FROM "Smith" S: * SEARCH 2 84 882 S: A282 OK SEARCH completed C: A283 SEARCH TEXT "string not in mailbox" S: * SEARCH S: A283 OK SEARCH completed C: A284 SEARCH CHARSET UTF-8 TEXT {6} S: + Continue ### THIS IS UNDUCUMENTED !!! C: XXXXXX S: * SEARCH 43 S: A284 OK SEARCH completed Note: Since this document is restricted to 7-bit ASCII text, it is not possible to show actual UTF-8 data. The "XXXXXX" is a placeholder for what would be 6 octets of 8-bit data in an actual transaction. */ if(!m_Authenticated){ m_pSocket.SendLine(cmdTag + " NO Authenticate first !"); return; } if(m_SelectedMailbox.Length == 0){ m_pSocket.SendLine(cmdTag + " NO Select mailbox first !"); return; } // Get Optional charset, if specified string charset = "ASCII"; // CHARSET charset if(argsText.ToUpper().StartsWith("CHARSET")){ // Remove CHARSET from argsText argsText = argsText.Substring(7).Trim(); string charsetValueString = IMAP_Utils.ParseQuotedParam(ref argsText);//argsText.Split(' ')[0]; try{ System.Text.Encoding.GetEncoding(charsetValueString); charset = charsetValueString; } catch{ m_pSocket.SendLine(cmdTag + " NO [BADCHARSET UTF-8] " + charsetValueString + " is not supported"); return; } // Remove charset value from args text // argsText = argsText.Substring(charset.Length).Trim(); } // Parse search keys Hashtable searchKeys = new Hashtable(); while(argsText.Length > 0){ // ALL if(argsText.ToUpper().StartsWith("ALL")){ // Remove ALL from argsText argsText = argsText.Substring(3).Trim(); // Just eat this keyword } // ANSWERED else if(argsText.ToUpper().StartsWith("ANSWERED")){ // Remove ANSWERED from argsText argsText = argsText.Substring(8).Trim(); // Move to KEYWORD <flag> searchKeys.Add("KEYWORD","ANSWERED"); } // BCC <string> or {<string>.Length} if charset specified else if(argsText.ToUpper().StartsWith("BCC")){ // Remove BCC from argsText argsText = argsText.Substring(3).Trim(); string paramValue = ""; if(!argsText.StartsWith("{")){ try{ paramValue = IMAP_Utils.ParseQuotedParam(ref argsText); } catch(Exception x){ m_pSocket.SendLine(cmdTag + " NO Invalid BCC <string> value: " + x.Message); return; } } else{ // get string length, it must be between {} long count = 0; if(argsText.StartsWith("{") && argsText.IndexOf('}',1) > -1){ try{ count = Convert.ToInt64(argsText.Substring(1,argsText.IndexOf('}',1) - 1)); } catch{ m_pSocket.SendLine(cmdTag + " NO Invalid BCC {x} value"); return; } } else{ m_pSocket.SendLine(cmdTag + " NO Invalid BCC value"); return; } // Notify connected client that it can continue sending cmdline m_pSocket.SendLine("+ Continue"); // Read string from socket MemoryStream ms = new MemoryStream(); ReadReplyCode result = m_pSocket.ReadData(ms,count,true); if(result != ReadReplyCode.Ok){ throw new Exception(result.ToString()); } paramValue = System.Text.Encoding.GetEncoding(charset).GetString(ms.ToArray()); // Get next args text argsText = m_pSocket.ReadLine(); } // Move to HEADER <field-name> <string> searchKeys.Add("HEADER",new string[]{"BCC",paramValue}); } // BEFORE <date> else if(argsText.ToUpper().StartsWith("BEFORE")){ // Remove BEFORE from argsText argsText = argsText.Substring(6).Trim(); string dateValueString = argsText.Split(' ')[0]; try{ DateTime dateValue = _SearchHelper.ParseDate(dateValueString); searchKeys.Add("BEFORE",dateValue); } catch{ m_pSocket.SendLine(cmdTag + " NO Invalid BEFORE <date> value"); return; } // Remove date value from argsText argsText = argsText.Substring(dateValueString.Length).Trim(); } // BODY <string> or {<string>.Length} if charset specified else if(argsText.ToUpper().StartsWith("BODY")){ // Remove BODY from argsText argsText = argsText.Substring(4).Trim(); string paramValue = ""; if(!argsText.StartsWith("{")){ try{ paramValue = IMAP_Utils.ParseQuotedParam(ref argsText); } catch(Exception x){ m_pSocket.SendLine(cmdTag + " NO Invalid BODY <string> value: " + x.Message); return; } } else{ // get string length, it must be between {} long count = 0; if(argsText.StartsWith("{") && argsText.IndexOf('}',1) > -1){ try{ count = Convert.ToInt64(argsText.Substring(1,argsText.IndexOf('}',1) - 1)); } catch{ m_pSocket.SendLine(cmdTag + " NO Invalid BODY {x} value"); return; } } else{ m_pSocket.SendLine(cmdTag + " NO Invalid BODY value"); return; } // Notify connected client that it can continue sending cmdline m_pSocket.SendLine("+ Continue"); // Read string from socket MemoryStream ms = new MemoryStream(); ReadReplyCode result = m_pSocket.ReadData(ms,count,true); if(result != ReadReplyCode.Ok){ throw new Exception(result.ToString()); } paramValue = System.Text.Encoding.GetEncoding(charset).GetString(ms.ToArray()); // Get next args text argsText = m_pSocket.ReadLine(); } searchKeys.Add("BODY",paramValue); } // CC <string> or {<string>.Length} if charset specified else if(argsText.ToUpper().StartsWith("CC")){ // Remove CC from argsText argsText = argsText.Substring(2).Trim(); string paramValue = ""; if(!argsText.StartsWith("{")){ try{ paramValue = IMAP_Utils.ParseQuotedParam(ref argsText); } catch(Exception x){ m_pSocket.SendLine(cmdTag + " NO Invalid CC <string> value: " + x.Message); return; } } else{ // get string length, it must be between {} long count = 0; if(argsText.StartsWith("{") && argsText.IndexOf('}',1) > -1){ try{ count = Convert.ToInt64(argsText.Substring(1,argsText.IndexOf('}',1) - 1)); } catch{ m_pSocket.SendLine(cmdTag + " NO Invalid CC {x} value"); return; } } else{ m_pSocket.SendLine(cmdTag + " NO Invalid CC value"); return; } // Notify connected client that it can continue sending cmdline m_pSocket.SendLine("+ Continue"); // Read string from socket MemoryStream ms = new MemoryStream(); ReadReplyCode result = m_pSocket.ReadData(ms,count,true); if(result != ReadReplyCode.Ok){ throw new Exception(result.ToString()); } paramValue = System.Text.Encoding.GetEncoding(charset).GetString(ms.ToArray()); // Get next args text argsText = m_pSocket.ReadLine(); } // Move to HEADER <field-name> <string> searchKeys.Add("HEADER",new string[]{"CC",paramValue}); } // DELETED else if(argsText.ToUpper().StartsWith("DELETED")){ // Remove DELETED from argsText argsText = argsText.Substring(7).Trim(); // Move to KEYWORD <flag> searchKeys.Add("KEYWORD","DELETED"); } // DRAFT else if(argsText.ToUpper().StartsWith("DRAFT")){ // Remove DRAFT from argsText argsText = argsText.Substring(5).Trim(); // Move to KEYWORD <flag> searchKeys.Add("KEYWORD","DRAFT"); } // FLAGGED else if(argsText.ToUpper().StartsWith("FLAGGED")){ // Remove FLAGGED from argsText argsText = argsText.Substring(7).Trim(); // Move to KEYWORD <flag> searchKeys.Add("KEYWORD","FLAGGED"); } // FROM <string> or {<string>.Length} if charset specified else if(argsText.ToUpper().StartsWith("FROM")){ // Remove FROM from argsText argsText = argsText.Substring(4).Trim(); string paramValue = ""; if(!argsText.StartsWith("{")){ try{ paramValue = IMAP_Utils.ParseQuotedParam(ref argsText); } catch(Exception x){ m_pSocket.SendLine(cmdTag + " NO Invalid FROM <string> value: " + x.Message); return; } } else{ // get string length, it must be between {} long count = 0; if(argsText.StartsWith("{") && argsText.IndexOf('}',1) > -1){ try{ count = Convert.ToInt64(argsText.Substring(1,argsText.IndexOf('}',1) - 1)); } catch{ m_pSocket.SendLine(cmdTag + " NO Invalid FROM {x} value"); return; } } else{ m_pSocket.SendLine(cmdTag + " NO Invalid FROM value"); return; } // Notify connected client that it can continue sending cmdline m_pSocket.SendLine("+ Continue"); // Read string from socket MemoryStream ms = new MemoryStream(); ReadReplyCode result = m_pSocket.ReadData(ms,count,true); if(result != ReadReplyCode.Ok){ throw new Exception(result.ToString()); } paramValue = System.Text.Encoding.GetEncoding(charset).GetString(ms.ToArray()); // Get next args text argsText = m_pSocket.ReadLine(); } // Move to HEADER <field-name> <string> searchKeys.Add("HEADER",new string[]{"FROM",paramValue}); } // HEADER <field-name> <string> or {<string>.Length} if charset specified else if(argsText.ToUpper().StartsWith("HEADER")){ // Remove HEADER from argsText argsText = argsText.Substring(6).Trim(); // Get field name string fieldName = IMAP_Utils.ParseQuotedParam(ref argsText); string paramValue = ""; if(!argsText.StartsWith("{")){ try{ paramValue = IMAP_Utils.ParseQuotedParam(ref argsText); } catch(Exception x){ m_pSocket.SendLine(cmdTag + " NO Invalid HEADER <string> value: " + x.Message); return; } } else{ // get string length, it must be between {} long count = 0; if(argsText.StartsWith("{") && argsText.IndexOf('}') > -1){ try{ count = Convert.ToInt64(argsText.Substring(1,argsText.IndexOf('}') - 1)); } catch{ m_pSocket.SendLine(cmdTag + " NO Invalid HEADER {x} value"); return; } } else{ m_pSocket.SendLine(cmdTag + " NO Invalid HEADER value"); return; } // Notify connected client that it can continue sending cmdline m_pSocket.SendLine("+ Continue"); // Read string from socket MemoryStream ms = new MemoryStream(); ReadReplyCode result = m_pSocket.ReadData(ms,count,true); if(result != ReadReplyCode.Ok){ throw new Exception(result.ToString()); } paramValue = System.Text.Encoding.GetEncoding(charset).GetString(ms.ToArray()); // Get next args text argsText = m_pSocket.ReadLine(); } searchKeys.Add("HEADER",new string[]{fieldName,paramValue}); } // KEYWORD <flag> else if(argsText.ToUpper().StartsWith("KEYWORD")){ // Remove KEYWORD from argsText argsText = argsText.Substring(7).Trim(); // Get flag value string flagValue = argsText.Split(' ')[0]; searchKeys.Add("KEYWORD",flagValue); // Remove flag value from argsText argsText = argsText.Substring(flagValue.Length).Trim(); } // LARGER <n> else if(argsText.ToUpper().StartsWith("LARGER")){ // Remove LARGER from argsText argsText = argsText.Substring(6).Trim(); long lagerValue = 0; try{ lagerValue = Convert.ToInt64(argsText.Split(' ')[0]); } catch{ } searchKeys.Add("LARGER",lagerValue); // Remove flag value from argsText argsText = argsText.Substring(lagerValue.ToString().Length).Trim(); } // NEW else if(argsText.ToUpper().StartsWith("NEW")){ // Remove NEW from argsText argsText = argsText.Substring(3).Trim(); // Move to KEYWORD <flag> searchKeys.Add("KEYWORD","RECENT"); } // NOT <search-key> else if(argsText.ToUpper().StartsWith("NOT")){ // Remove NOT from argsText argsText = argsText.Substring(3).Trim(); m_pSocket.SendLine(cmdTag + " NO NOT search key isn't supported at moment"); return; } // OLD else if(argsText.ToUpper().StartsWith("OLD")){ // Remove OLD from argsText argsText = argsText.Substring(3).Trim(); // Move to KEYWORD <flag> searchKeys.Add("UNKEYWORD","RECENT"); } // ON <date> else if(argsText.ToUpper().StartsWith("ON")){ // Remove ON from argsText argsText = argsText.Substring(2).Trim(); string dateValueString = argsText.Split(' ')[0]; try{ DateTime dateValue = _SearchHelper.ParseDate(dateValueString); searchKeys.Add("ON",dateValue); } catch{ m_pSocket.SendLine(cmdTag + " NO Invalid ON <date> value"); return; } // Remove date value from argsText argsText = argsText.Substring(dateValueString.Length).Trim(); } // OR <search-key1> <search-key2> else if(argsText.ToUpper().StartsWith("OR")){ // Remove OR from argsText argsText = argsText.Substring(2).Trim(); m_pSocket.SendLine(cmdTag + " NO OR search key isn't supported at moment"); return; } // RECENT else if(argsText.ToUpper().StartsWith("RECENT")){ // Remove RECENT from argsText argsText = argsText.Substring(6).Trim(); // Move to KEYWORD <flag> searchKeys.Add("KEYWORD","RECENT"); } // SEEN else if(argsText.ToUpper().StartsWith("SEEN")){ // Remove SEEN from argsText argsText = argsText.Substring(4).Trim(); // Move to KEYWORD <flag> searchKeys.Add("KEYWORD","SEEN"); } // SENTBEFORE <date> else if(argsText.ToUpper().StartsWith("SENTBEFORE")){ // Remove SENTBEFORE from argsText argsText = argsText.Substring(10).Trim(); string dateValueString = argsText.Split(' ')[0]; try{ DateTime dateValue = _SearchHelper.ParseDate(dateValueString); searchKeys.Add("SENTBEFORE",dateValue); } catch{ m_pSocket.SendLine(cmdTag + " NO Invalid SENTBEFORE <date> value"); return; } // Remove date value from argsText argsText = argsText.Substring(dateValueString.Length).Trim(); } // SENTON <date> else if(argsText.ToUpper().StartsWith("SENTON")){ // Remove SENTON from argsText argsText = argsText.Substring(6).Trim(); string dateValueString = argsText.Split(' ')[0]; try{ DateTime dateValue = _SearchHelper.ParseDate(dateValueString); searchKeys.Add("SENTON",dateValue); } catch{ m_pSocket.SendLine(cmdTag + " NO Invalid SENTON <date> value"); return; } // Remove date value from argsText argsText = argsText.Substring(dateValueString.Length).Trim(); } // SENTSINCE <date> else if(argsText.ToUpper().StartsWith("SENTSINCE")){ // Remove SENTSINCE from argsText argsText = argsText.Substring(9).Trim(); string dateValueString = argsText.Split(' ')[0]; try{ DateTime dateValue = _SearchHelper.ParseDate(dateValueString); searchKeys.Add("SENTSINCE",dateValue); } catch{ m_pSocket.SendLine(cmdTag + " NO Invalid SENTSINCE <date> value"); return; } // Remove date value from argsText argsText = argsText.Substring(dateValueString.Length).Trim(); } // SINCE <date> else if(argsText.ToUpper().StartsWith("SINCE")){ // Remove SINCE from argsText argsText = argsText.Substring(5).Trim(); string dateValueString = argsText.Split(' ')[0]; try{ DateTime dateValue = _SearchHelper.ParseDate(dateValueString); searchKeys.Add("SINCE",dateValue); } catch{ m_pSocket.SendLine(cmdTag + " NO Invalid SINCE <date> value"); return; } // Remove date value from argsText argsText = argsText.Substring(dateValueString.Length).Trim(); } // SMALLER <n> else if(argsText.ToUpper().StartsWith("SMALLER")){ // Remove SMALLER from argsText argsText = argsText.Substring(7).Trim(); long smallerValue = 0; try{ smallerValue = Convert.ToInt64(argsText.Split(' ')[0]); } catch{ } searchKeys.Add("SMALLER",smallerValue); // Remove flag value from argsText argsText = argsText.Substring(smallerValue.ToString().Length).Trim(); } // SUBJECT <string> or {<string>.Length} if charset specified else if(argsText.ToUpper().StartsWith("SUBJECT")){ // Remove SUBJECT from argsText argsText = argsText.Substring(7).Trim(); string paramValue = ""; if(!argsText.StartsWith("{")){ try{ paramValue = IMAP_Utils.ParseQuotedParam(ref argsText); } catch(Exception x){ m_pSocket.SendLine(cmdTag + " NO Invalid SUBJECT <string> value: " + x.Message); return; } } else{ // get string length, it must be between {} long count = 0; if(argsText.StartsWith("{") && argsText.IndexOf('}',1) > -1){ try{ count = Convert.ToInt64(argsText.Substring(1,argsText.IndexOf('}',1) - 1)); } catch{ m_pSocket.SendLine(cmdTag + " NO Invalid SUBJECT {x} value"); return; } } else{ m_pSocket.SendLine(cmdTag + " NO Invalid SUBJECT value"); return; } // Notify connected client that it can continue sending cmdline m_pSocket.SendLine("+ Continue"); // Read string from socket MemoryStream ms = new MemoryStream(); ReadReplyCode result = m_pSocket.ReadData(ms,count,true); if(result != ReadReplyCode.Ok){ throw new Exception(result.ToString()); } paramValue = System.Text.Encoding.GetEncoding(charset).GetString(ms.ToArray()); // Get next args text argsText = m_pSocket.ReadLine(); } // Move to HEADER <field-name> <string> searchKeys.Add("HEADER",new string[]{"SUBJECT",paramValue}); } // TEXT <string> or {<string>.Length} if charset specified else if(argsText.ToUpper().StartsWith("TEXT")){ // Remove TEXT from argsText argsText = argsText.Substring(4).Trim(); string paramValue = ""; if(!argsText.StartsWith("{")){ try{ paramValue = IMAP_Utils.ParseQuotedParam(ref argsText); } catch(Exception x){ m_pSocket.SendLine(cmdTag + " NO Invalid TEXT <string> value: " + x.Message); return; } } else{ // get string length, it must be between {} long count = 0; if(argsText.StartsWith("{") && argsText.IndexOf('}',1) > -1){ try{ count = Convert.ToInt64(argsText.Substring(1,argsText.IndexOf('}',1) - 1)); } catch{ m_pSocket.SendLine(cmdTag + " NO Invalid TEXT {x} value"); return; } } else{ m_pSocket.SendLine(cmdTag + " NO Invalid TEXT value"); return; } // Notify connected client that it can continue sending cmdline m_pSocket.SendLine("+ Continue"); // Read string from socket MemoryStream ms = new MemoryStream(); ReadReplyCode result = m_pSocket.ReadData(ms,count,true); if(result != ReadReplyCode.Ok){ throw new Exception(result.ToString()); } paramValue = System.Text.Encoding.GetEncoding(charset).GetString(ms.ToArray()); // Get next args text argsText = m_pSocket.ReadLine(); } searchKeys.Add("TEXT",paramValue); } // TO <string> or {<string>.Length} if charset specified else if(argsText.ToUpper().StartsWith("TO")){ // Remove TO from argsText argsText = argsText.Substring(2).Trim(); string paramValue = ""; if(!argsText.StartsWith("{")){ try{ paramValue = IMAP_Utils.ParseQuotedParam(ref argsText); } catch(Exception x){ m_pSocket.SendLine(cmdTag + " NO Invalid TO <string> value: " + x.Message); return; } } else{ // get string lenght, it must be between {} long count = 0; if(argsText.StartsWith("{") && argsText.IndexOf('}',1) > -1){ try{ count = Convert.ToInt64(argsText.Substring(1,argsText.IndexOf('}',1) - 1)); } catch{ m_pSocket.SendLine(cmdTag + " NO Invalid TO {x} value"); return; } } else{ m_pSocket.SendLine(cmdTag + " NO Invalid TO value"); return; } // Notify connected client that it can continue sending cmdline m_pSocket.SendLine("+ Continue"); // Read string from socket MemoryStream ms = new MemoryStream(); ReadReplyCode result = m_pSocket.ReadData(ms,count,true); if(result != ReadReplyCode.Ok){ throw new Exception(result.ToString()); } paramValue = System.Text.Encoding.GetEncoding(charset).GetString(ms.ToArray()); // Get next args text argsText = m_pSocket.ReadLine(); } // Move to HEADER <field-name> <string> searchKeys.Add("HEADER",new string[]{"TO",paramValue}); } // UID <sequence set> else if(argsText.ToUpper().StartsWith("UID")){ // Remove UID from argsText argsText = argsText.Substring(3).Trim(); string uidValue = argsText.Split(' ')[0]; searchKeys.Add("UID",uidValue); // Remove uid value from argsText argsText = argsText.Substring(uidValue.Length).Trim(); } // UNANSWERED else if(argsText.ToUpper().StartsWith("UNANSWERED")){ // Remove UNANSWERED from argsText argsText = argsText.Substring(10).Trim(); // Move to UNKEYWORD <flag> searchKeys.Add("UNKEYWORD","ANSWERED"); } // UNDELETED else if(argsText.ToUpper().StartsWith("UNDELETED")){ // Remove UNDELETED from argsText argsText = argsText.Substring(9).Trim(); // Move to UNKEYWORD <flag> searchKeys.Add("UNKEYWORD","DELETED"); } // UNDRAFT else if(argsText.ToUpper().StartsWith("UNDRAFT")){ // Remove UNDRAFT from argsText argsText = argsText.Substring(7).Trim(); // Move to UNKEYWORD <flag> searchKeys.Add("UNKEYWORD","DRAFT"); } // UNFLAGGED else if(argsText.ToUpper().StartsWith("UNFLAGGED")){ // Remove UNFLAGGED from argsText argsText = argsText.Substring(9).Trim(); // Move to UNKEYWORD <flag> searchKeys.Add("UNKEYWORD","FLAGGED"); } // UNKEYWORD <flag> else if(argsText.ToUpper().StartsWith("UNKEYWORD")){ } // UNSEEN else if(argsText.ToUpper().StartsWith("UNSEEN")){ // Remove UNSEEN from argsText argsText = argsText.Substring(6).Trim(); // Move to UNKEYWORD <flag> searchKeys.Add("UNKEYWORD","SEEN"); } // This is is unkown search key else{ m_pSocket.SendLine(cmdTag + " NO Invalid search key " + argsText.Split(' ')[0]); return; } } // Just loop messages headers or full messages (depends on search type) string searchResponse = "* SEARCH"; for(int i=0;i<m_Messages.Count;i++){ IMAP_Message msg = m_Messages[i]; byte[] msgData = null; Message_EventArgs eArgs = m_pServer.OnGetMessage(this,msg,false); msgData = eArgs.MessageData; MimeParser parser = new MimeParser(msgData); bool matches = true; foreach(DictionaryEntry ent in searchKeys){ matches = _SearchHelper.MatchSearchKey(ent.Key.ToString(),ent.Value,msg,parser); if(!matches){ matches = false; break; } } if(!matches){ continue; } // If we reached so far, then message matches search criteria if(uidSearch){ searchResponse += " " + msg.MessageUID.ToString(); } else{ searchResponse += " " + msg.MessageNo.ToString(); } } searchResponse += "\r\n"; searchResponse += cmdTag + " OK SEARCH completed\r\n"; m_pSocket.SendData(searchResponse); }
/// <summary> /// Gets top lines of message. /// </summary> /// <param name="nr">Message number which top lines to get.</param> /// <param name="nLines">Number of lines to get.</param> public System.Net.Mail.MailMessage GetTopOfMessage(int nr, int nLines) { if (!m_Connected) { throw new Exception("You must connect first !"); } if (!m_Authenticated) { throw new Exception("You must authenticate first !"); } streamHelper.Write("TOP " + nr.ToString() + " " + nLines.ToString() + "\r\n"); // Read first line of reply, check if it's ok string line = streamHelper.ReadToEnd(); if (line.StartsWith("+OK")) { MimeParser parser = new MimeParser(Core.DoPeriodHandling(socketStream, false, false).ToArray()); return parser.ToMailMessage(); } else { throw new Exception("Server returned:" + line); } }
/// <summary> /// Constructs FETCH BODY and BODYSTRUCTURE response. /// </summary> /// <param name="parser"></param> /// <param name="bodystructure"></param> /// <returns></returns> public static string ConstructBodyStructure(MimeParser parser,bool bodystructure) { /* Rfc 3501 7.4.2 BODYSTRUCTURE For example, a simple text message of 48 lines and 2279 octets can have a body structure of: ("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 2279 48) For example, a two part message consisting of a text and a BASE64-encoded text attachment can have a body structure of: (("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 1152 23)("TEXT" "PLAIN" ("CHARSET" "US-ASCII" "NAME" "cc.diff") "<*****@*****.**>" "Compiler diff" "BASE64" 4554 73) "MIXED") // Basic fields for multipart (nestedMimeEntries) conentSubType // Extention data for multipart (conentTypeSubFields) contentDisposition contentLanguage [contentLocation] contentDisposition - ("disposition" {(subFileds) or NIL}) or NIL contentType - 'TEXT' conentSubType - 'PLAIN' conentTypeSubFields - '("CHARSET" "iso-8859-1" ...)' contentID - Content-ID field contentDescription - Content-Description field contentEncoding - 'quoted-printable' contentSize - mimeEntry NOT ENCODED data size [envelope] - NOTE: included only if contentType = "message" !!! [contentLines] - number of content lines. NOTE: included only if contentType = "text" !!! // Basic fields for non-multipart contentType conentSubType (conentTypeSubFields) contentID contentDescription contentEncoding contentSize contentLines // Extention data for non-multipart contentDataMd5 contentDisposition contentLanguage [conentLocation] body language A string or parenthesized list giving the body language value as defined in [LANGUAGE-TAGS]. body location A string list giving the body content URI as defined in [LOCATION]. */ string str = ""; if(bodystructure){ str += "BODYSTRUCTURE "; } else{ str += "BODY "; } if(parser.ContentType.ToLower().IndexOf("multipart") > -1){ str += "("; } str += ConstructPart(parser.MimeEntries,bodystructure); if(parser.ContentType.ToLower().IndexOf("multipart") > -1){ // conentSubType if(parser.ContentType.Split('/').Length == 2){ str += " \"" + parser.ContentType.Split('/')[1].Replace(";","") + "\""; } else{ str += " NIL"; } // Need to add extended fields if(bodystructure){ str += " "; // conentTypeSubFields string longContentType = MimeParser.ParseHeaderField("Content-Type:",parser.Headers); if(longContentType.IndexOf(";") > -1){ str += "("; string[] fields = longContentType.Split(';'); for(int i=1;i<fields.Length;i++){ string[] nameValue = fields[i].Replace("\"","").Trim().Split(new char[]{'='},2); str += "\"" + nameValue[0] + "\" \"" + nameValue[1] + "\""; if(i < fields.Length - 1){ str += " "; } } str += ") "; } // contentDisposition str += "NIL "; // contentLanguage str += "NIL"; } str += ")"; } return str; }
/// <summary> /// Construct FETCH ENVELOPE response. /// </summary> /// <param name="parser"></param> /// <returns></returns> public static string ConstructEnvelope(MimeParser parser) { /* Rfc 3501 7.4.2 ENVELOPE A parenthesized list that describes the envelope structure of a message. This is computed by the server by parsing the [RFC-2822] header into the component parts, defaulting various fields as necessary. The fields of the envelope structure are in the following order: date, subject, from, sender, reply-to, to, cc, bcc, in-reply-to, and message-id. The date, subject, in-reply-to, and message-id fields are strings. The from, sender, reply-to, to, cc, and bcc fields are parenthesized lists of address structures. An address structure is a parenthesized list that describes an electronic mail address. The fields of an address structure are in the following order: personal name, [SMTP] at-domain-list (source route), mailbox name, and host name. [RFC-2822] group syntax is indicated by a special form of address structure in which the host name field is NIL. If the mailbox name field is also NIL, this is an end of group marker (semi-colon in RFC 822 syntax). If the mailbox name field is non-NIL, this is a start of group marker, and the mailbox name field holds the group name phrase. If the Date, Subject, In-Reply-To, and Message-ID header lines are absent in the [RFC-2822] header, the corresponding member of the envelope is NIL; if these header lines are present but empty the corresponding member of the envelope is the empty string. */ // ((sender)) // ENVELOPE ("date" "subject" from sender reply-to to cc bcc in-reply-to "messageID") string envelope = "ENVELOPE ("; // date envelope += "\"" + parser.MessageDate.ToString("r",System.Globalization.DateTimeFormatInfo.InvariantInfo) + "\" "; // subject envelope += "\"" + Escape(parser.Subject) + "\" "; // from // ToDo: May be multiple senders InfoControl.Net.Mail.Mime.eAddress adr = new InfoControl.Net.Mail.Mime.eAddress(parser.From); envelope += "((\"" + Escape(adr.Name) + "\" NIL \"" + Escape(adr.Mailbox) + "\" \"" + Escape(adr.Domain) + "\")) "; // sender // ToDo: May be multiple senders envelope += "((\"" + Escape(adr.Name) + "\" NIL \"" + Escape(adr.Mailbox) + "\" \"" + Escape(adr.Domain) + "\")) "; // reply-to string replyTo = MimeParser.ParseHeaderField("reply-to:",parser.Headers); if(replyTo.Length > 0){ envelope += "("; foreach(string recipient in replyTo.Split(';')){ InfoControl.Net.Mail.Mime.eAddress adrTo = new InfoControl.Net.Mail.Mime.eAddress(recipient); envelope += "(\"" + Escape(adrTo.Name) + "\" NIL \"" + Escape(adrTo.Mailbox) + "\" \"" + Escape(adrTo.Domain) + "\") "; } envelope = envelope.TrimEnd(); envelope += ") "; } else{ envelope += "NIL "; } // to string[] to = parser.To; envelope += "("; foreach(string recipient in to){ InfoControl.Net.Mail.Mime.eAddress adrTo = new InfoControl.Net.Mail.Mime.eAddress(recipient); envelope += "(\"" + Escape(adrTo.Name) + "\" NIL \"" + Escape(adrTo.Mailbox) + "\" \"" + Escape(adrTo.Domain) + "\") "; } envelope = envelope.TrimEnd(); envelope += ") "; // cc string cc = MimeParser.ParseHeaderField("CC:",parser.Headers); if(cc.Length > 0){ envelope += "("; foreach(string recipient in cc.Split(';')){ InfoControl.Net.Mail.Mime.eAddress adrTo = new InfoControl.Net.Mail.Mime.eAddress(recipient); envelope += "(\"" + Escape(adrTo.Name) + "\" NIL \"" + Escape(adrTo.Mailbox) + "\" \"" + Escape(adrTo.Domain) + "\") "; } envelope = envelope.TrimEnd(); envelope += ") "; } else{ envelope += "NIL "; } // bcc string bcc = MimeParser.ParseHeaderField("BCC:",parser.Headers); if(bcc.Length > 0){ envelope += "("; foreach(string recipient in bcc.Split(';')){ InfoControl.Net.Mail.Mime.eAddress adrTo = new InfoControl.Net.Mail.Mime.eAddress(recipient); envelope += "(\"" + Escape(adrTo.Name) + "\" NIL \"" + Escape(adrTo.Mailbox) + "\" \"" + Escape(adrTo.Domain) + "\") "; } envelope = envelope.TrimEnd(); envelope += ") "; } else{ envelope += "NIL "; } // in-reply-to string inReplyTo = MimeParser.ParseHeaderField("in-reply-to:",parser.Headers); if(inReplyTo.Length > 0){ envelope += "\"" + Escape(inReplyTo) + "\" "; } else{ envelope += "NIL "; } // message-id if(parser.MessageID.Length > 0){ envelope += "\"" + Escape(parser.MessageID) + "\""; } else{ envelope += "NIL"; } envelope += ")"; return envelope; }
/// <summary> /// Returns requested mime entry data. /// </summary> /// <param name="parser"></param> /// <param name="mimeEntryNo"></param> /// <returns>Returns requested mime entry data or NULL if requested entri doesn't exist.</returns> public static byte[] ParseMimeEntry(MimeParser parser,string mimeEntryNo) { MimeEntry mEntry = null; string[] parts = mimeEntryNo.Split('.'); foreach(string part in parts){ int mEntryNo = Convert.ToInt32(part); if(mEntry == null){ if(mEntryNo > 0 && mEntryNo <= parser.MimeEntries.Count){ mEntry = ((MimeEntry)parser.MimeEntries[mEntryNo - 1]); } else{ return null; } } else{ if(mEntryNo > 0 && mEntryNo <= mEntry.MimeEntries.Count){ mEntry = ((MimeEntry)mEntry.MimeEntries[mEntryNo - 1]); } else{ return null; } } } if(mEntry != null){ return mEntry.DataNonDecoded; } else{ return null; } }