/// <summary> /// Sets body data from the specified stream. /// </summary> /// <param name="stream">Source stream.</param> /// <param name="transferEncoding">Specifies content-transfer-encoding to use to encode data.</param> /// <exception cref="ArgumentNullException">Is raised when <b>stream</b> or <b>transferEncoding</b> is null reference.</exception> /// <exception cref="InvalidOperationException">Is raised when this method is accessed and this body is not bounded to any entity.</exception> public void SetData(Stream stream,string transferEncoding) { if(stream == null){ throw new ArgumentNullException("stream"); } if(transferEncoding == null){ throw new ArgumentNullException("transferEncoding"); } if(string.Equals(transferEncoding,MIME_TransferEncodings.QuotedPrintable,StringComparison.InvariantCultureIgnoreCase)){ using(MemoryStreamEx fs = new MemoryStreamEx(32000)){ QuotedPrintableStream encoder = new QuotedPrintableStream(new SmartStream(fs,false),FileAccess.ReadWrite); Net_Utils.StreamCopy(stream,encoder,32000); encoder.Flush(); fs.Position = 0; SetEncodedData(transferEncoding,fs); } } else if(string.Equals(transferEncoding,MIME_TransferEncodings.Base64,StringComparison.InvariantCultureIgnoreCase)){ using(MemoryStreamEx fs = new MemoryStreamEx(32000)){ Base64Stream encoder = new Base64Stream(fs,false,true,FileAccess.ReadWrite); Net_Utils.StreamCopy(stream,encoder,32000); encoder.Finish(); fs.Position = 0; SetEncodedData(transferEncoding,fs); } } else if(string.Equals(transferEncoding,MIME_TransferEncodings.Binary,StringComparison.InvariantCultureIgnoreCase)){ SetEncodedData(transferEncoding,stream); } else if(string.Equals(transferEncoding,MIME_TransferEncodings.EightBit,StringComparison.InvariantCultureIgnoreCase)){ SetEncodedData(transferEncoding,stream); } else if(string.Equals(transferEncoding,MIME_TransferEncodings.SevenBit,StringComparison.InvariantCultureIgnoreCase)){ SetEncodedData(transferEncoding,stream); } else{ throw new NotSupportedException("Not supported Content-Transfer-Encoding '" + transferEncoding + "'."); } }
// #region method FETCH private void FETCH(bool uid,string cmdTag,string cmdText) { /* RFC 3501. 6.4.5. FETCH Command. Arguments: sequence set message data item names or macro 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 */ // Store start time long startTime = DateTime.Now.Ticks; if(!this.IsAuthenticated){ m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus(cmdTag,"NO","Authentication required.")); return; } if(m_pSelectedFolder == null){ m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus(cmdTag,"NO","Error: This command is valid only in selected state.")); return; } string[] parts = cmdText.Split(new char[]{' '},2); if(parts.Length != 2){ m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus(cmdTag,"BAD","Error in arguments.")); return; } IMAP_t_SeqSet seqSet = null; try{ seqSet = IMAP_t_SeqSet.Parse(parts[0]); } catch{ m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus(cmdTag,"BAD","Error in arguments: Invalid 'sequence-set' value.")); return; } #region Parse data-items List<IMAP_t_Fetch_i> dataItems = new List<IMAP_t_Fetch_i>(); bool msgDataNeeded = false; // Remove parenthesizes. string dataItemsString = parts[1].Trim(); if(dataItemsString.StartsWith("(") && dataItemsString.EndsWith(")")){ dataItemsString = dataItemsString.Substring(1,dataItemsString.Length - 2).Trim(); } // Replace macros. dataItemsString = dataItemsString.Replace("ALL","FLAGS INTERNALDATE RFC822.SIZE ENVELOPE"); dataItemsString = dataItemsString.Replace("FAST","FLAGS INTERNALDATE RFC822.SIZE"); dataItemsString = dataItemsString.Replace("FULL","FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY"); StringReader r = new StringReader(dataItemsString); IMAP_Fetch_DataType fetchDataType = IMAP_Fetch_DataType.MessageHeader; // Parse data-items. while(r.Available > 0){ r.ReadToFirstChar(); #region BODYSTRUCTURE if(r.StartsWith("BODYSTRUCTURE",false)){ r.ReadWord(); dataItems.Add(new IMAP_t_Fetch_i_BodyStructure()); msgDataNeeded = true; if(fetchDataType != IMAP_Fetch_DataType.FullMessage){ fetchDataType = IMAP_Fetch_DataType.MessageStructure; } } #endregion #region BODY[<section>]<<partial>> and BODY.PEEK[<section>]<<partial>> else if(r.StartsWith("BODY[",false) || r.StartsWith("BODY.PEEK[",false)){ bool peek = r.StartsWith("BODY.PEEK[",false); r.ReadWord(); #region Parse <section> string section = r.ReadParenthesized(); // Full message wanted. if(string.IsNullOrEmpty(section)){ fetchDataType = IMAP_Fetch_DataType.FullMessage; } else{ // Left-side part-items must be numbers, only last one may be (HEADER,HEADER.FIELDS,HEADER.FIELDS.NOT,MIME,TEXT). StringReader rSection = new StringReader(section); string remainingSection = rSection.ReadWord(); while(remainingSection.Length > 0){ string[] section_parts = remainingSection.Split(new char[]{'.'},2); // Not part number. if(!Net_Utils.IsInteger(section_parts[0])){ // We must have one of the following values here (HEADER,HEADER.FIELDS,HEADER.FIELDS.NOT,MIME,TEXT). if(remainingSection.Equals("HEADER",StringComparison.InvariantCultureIgnoreCase)){ if(fetchDataType != IMAP_Fetch_DataType.FullMessage && fetchDataType != IMAP_Fetch_DataType.MessageStructure){ fetchDataType = IMAP_Fetch_DataType.MessageHeader; } } else if(remainingSection.Equals("HEADER.FIELDS",StringComparison.InvariantCultureIgnoreCase)){ rSection.ReadToFirstChar(); if(!rSection.StartsWith("(")){ WriteLine(cmdTag + " BAD Error in arguments."); return; } rSection.ReadParenthesized(); if(fetchDataType != IMAP_Fetch_DataType.FullMessage && fetchDataType != IMAP_Fetch_DataType.MessageStructure){ fetchDataType = IMAP_Fetch_DataType.MessageHeader; } } else if(remainingSection.Equals("HEADER.FIELDS.NOT",StringComparison.InvariantCultureIgnoreCase)){ rSection.ReadToFirstChar(); if(!rSection.StartsWith("(")){ WriteLine(cmdTag + " BAD Error in arguments."); return; } rSection.ReadParenthesized(); if(fetchDataType != IMAP_Fetch_DataType.FullMessage && fetchDataType != IMAP_Fetch_DataType.MessageStructure){ fetchDataType = IMAP_Fetch_DataType.MessageHeader; } } else if(remainingSection.Equals("MIME",StringComparison.InvariantCultureIgnoreCase)){ fetchDataType = IMAP_Fetch_DataType.FullMessage; } else if(remainingSection.Equals("TEXT",StringComparison.InvariantCultureIgnoreCase)){ fetchDataType = IMAP_Fetch_DataType.FullMessage; } // Unknown parts specifier. else{ WriteLine(cmdTag + " BAD Error in arguments."); return; } break; } else{ // For parts specifier, minimum is message structure. if(fetchDataType != IMAP_Fetch_DataType.FullMessage){ fetchDataType = IMAP_Fetch_DataType.MessageStructure; } } if(section_parts.Length == 2){ remainingSection = section_parts[1]; } else{ remainingSection = ""; } } } #endregion #region Parse <partial> int offset = -1; int maxCount = -1; // Partial data wanted. if(r.StartsWith("<")){ string[] origin = r.ReadParenthesized().Split('.'); if(origin.Length > 2){ WriteLine(cmdTag + " BAD Error in arguments."); return; } if(!int.TryParse(origin[0],out offset)){ WriteLine(cmdTag + " BAD Error in arguments."); return; } if(origin.Length == 2){ if(!int.TryParse(origin[1],out maxCount)){ WriteLine(cmdTag + " BAD Error in arguments."); return; } } } #endregion if(peek){ dataItems.Add(new IMAP_t_Fetch_i_BodyPeek(section,offset,maxCount)); } else{ dataItems.Add(new IMAP_t_Fetch_i_Body(section,offset,maxCount)); } msgDataNeeded = true; } #endregion #region BODY else if(r.StartsWith("BODY",false)){ r.ReadWord(); dataItems.Add(new IMAP_t_Fetch_i_BodyS()); msgDataNeeded = true; if(fetchDataType != IMAP_Fetch_DataType.FullMessage){ fetchDataType = IMAP_Fetch_DataType.MessageStructure; } } #endregion #region ENVELOPE else if(r.StartsWith("ENVELOPE",false)){ r.ReadWord(); dataItems.Add(new IMAP_t_Fetch_i_Envelope()); msgDataNeeded = true; if(fetchDataType != IMAP_Fetch_DataType.FullMessage && fetchDataType != IMAP_Fetch_DataType.MessageStructure){ fetchDataType = IMAP_Fetch_DataType.MessageHeader; } } #endregion #region FLAGS else if(r.StartsWith("FLAGS",false)){ r.ReadWord(); dataItems.Add(new IMAP_t_Fetch_i_Flags()); } #endregion #region INTERNALDATE else if(r.StartsWith("INTERNALDATE",false)){ r.ReadWord(); dataItems.Add(new IMAP_t_Fetch_i_InternalDate()); } #endregion #region RFC822.HEADER else if(r.StartsWith("RFC822.HEADER",false)){ r.ReadWord(); dataItems.Add(new IMAP_t_Fetch_i_Rfc822Header()); msgDataNeeded = true; if(fetchDataType != IMAP_Fetch_DataType.FullMessage && fetchDataType != IMAP_Fetch_DataType.MessageStructure){ fetchDataType = IMAP_Fetch_DataType.MessageHeader; } } #endregion #region RFC822.SIZE else if(r.StartsWith("RFC822.SIZE",false)){ r.ReadWord(); dataItems.Add(new IMAP_t_Fetch_i_Rfc822Size()); } #endregion #region RFC822.TEXT else if(r.StartsWith("RFC822.TEXT",false)){ r.ReadWord(); dataItems.Add(new IMAP_t_Fetch_i_Rfc822Text()); msgDataNeeded = true; fetchDataType = IMAP_Fetch_DataType.FullMessage; } #endregion #region RFC822 else if(r.StartsWith("RFC822",false)){ r.ReadWord(); dataItems.Add(new IMAP_t_Fetch_i_Rfc822()); msgDataNeeded = true; fetchDataType = IMAP_Fetch_DataType.FullMessage; } #endregion #region UID else if(r.StartsWith("UID",false)){ r.ReadWord(); dataItems.Add(new IMAP_t_Fetch_i_Uid()); } #endregion #region Unknown data-item. else{ WriteLine(cmdTag + " BAD Error in arguments: Unknown FETCH data-item."); return; } #endregion } #endregion // UID FETCH must always return UID data-item, even if user didn't request it. if(uid){ bool add = true; foreach(IMAP_t_Fetch_i item in dataItems){ if(item is IMAP_t_Fetch_i_Uid){ add = false; break; } } if(add){ dataItems.Add(new IMAP_t_Fetch_i_Uid()); } } UpdateSelectedFolderAndSendChanges(); IMAP_e_Fetch fetchEArgs = new IMAP_e_Fetch( m_pSelectedFolder.Filter(uid,seqSet), fetchDataType, new IMAP_r_ServerStatus(cmdTag,"OK","FETCH command completed in %exectime seconds.") ); fetchEArgs.NewMessageData += new EventHandler<IMAP_e_Fetch.e_NewMessageData>(delegate(object s,IMAP_e_Fetch.e_NewMessageData e){ StringBuilder reponseBuffer = new StringBuilder(); reponseBuffer.Append("* " + e.MessageInfo.SeqNo + " FETCH ("); Mail_Message message = e.MessageData; // Return requested data-items for the returned message. for(int i=0;i<dataItems.Count;i++){ IMAP_t_Fetch_i dataItem = dataItems[i]; // Add data-items separator. if(i > 0){ reponseBuffer.Append(" "); } #region BODY if(dataItem is IMAP_t_Fetch_i_BodyS){ reponseBuffer.Append(ConstructBodyStructure(message,false)); } #endregion #region BODY[<section>]<<partial>> and BODY.PEEK[<section>]<<partial>> else if(dataItem is IMAP_t_Fetch_i_Body || dataItem is IMAP_t_Fetch_i_BodyPeek){ string section = ""; int offset = -1; int maxCount = -1; if(dataItem is IMAP_t_Fetch_i_Body){ section = ((IMAP_t_Fetch_i_Body)dataItem).Section; offset = ((IMAP_t_Fetch_i_Body)dataItem).Offset; maxCount = ((IMAP_t_Fetch_i_Body)dataItem).MaxCount; } else{ section = ((IMAP_t_Fetch_i_BodyPeek)dataItem).Section; offset = ((IMAP_t_Fetch_i_BodyPeek)dataItem).Offset; maxCount = ((IMAP_t_Fetch_i_BodyPeek)dataItem).MaxCount; } using(MemoryStreamEx tmpFs = new MemoryStreamEx(32000)){ // Empty section, full message wanted. if(string.IsNullOrEmpty(section)){ message.ToStream(tmpFs,new MIME_Encoding_EncodedWord(MIME_EncodedWordEncoding.B,Encoding.UTF8),Encoding.UTF8); tmpFs.Position = 0; } // Message data part wanted. else{ // Get specified MIME part. MIME_Entity entity = GetMimeEntity(message,ParsePartNumberFromSection(section)); if(entity != null){ string partSpecifier = ParsePartSpecifierFromSection(section); #region HEADER if(string.Equals(partSpecifier,"HEADER",StringComparison.InvariantCultureIgnoreCase)){ entity.Header.ToStream(tmpFs,new MIME_Encoding_EncodedWord(MIME_EncodedWordEncoding.B,Encoding.UTF8),Encoding.UTF8); // All header fetches must include header terminator(CRLF). if(tmpFs.Length >0 ){ tmpFs.WriteByte((byte)'\r'); tmpFs.WriteByte((byte)'\n'); } tmpFs.Position = 0; } #endregion #region HEADER.FIELDS else if(string.Equals(partSpecifier,"HEADER.FIELDS",StringComparison.InvariantCultureIgnoreCase)){ string fieldsString = section.Split(new char[]{' '},2)[1]; string[] fieldNames = fieldsString.Substring(1,fieldsString.Length - 2).Split(' '); foreach(string filedName in fieldNames){ MIME_h[] fields = entity.Header[filedName]; if(fields != null){ foreach(MIME_h field in fields){ byte[] fieldBytes = Encoding.UTF8.GetBytes(field.ToString(new MIME_Encoding_EncodedWord(MIME_EncodedWordEncoding.B,Encoding.UTF8),Encoding.UTF8)); tmpFs.Write(fieldBytes,0,fieldBytes.Length); } } } // All header fetches must include header terminator(CRLF). if(tmpFs.Length > 0){ tmpFs.WriteByte((byte)'\r'); tmpFs.WriteByte((byte)'\n'); } tmpFs.Position = 0; } #endregion #region HEADER.FIELDS.NOT else if(string.Equals(partSpecifier,"HEADER.FIELDS.NOT",StringComparison.InvariantCultureIgnoreCase)){ string fieldsString = section.Split(new char[]{' '},2)[1]; string[] fieldNames = fieldsString.Substring(1,fieldsString.Length - 2).Split(' '); foreach(MIME_h field in entity.Header){ bool contains = false; foreach(string fieldName in fieldNames){ if(string.Equals(field.Name,fieldName,StringComparison.InvariantCultureIgnoreCase)){ contains = true; break; } } if(!contains){ byte[] fieldBytes = Encoding.UTF8.GetBytes(field.ToString(new MIME_Encoding_EncodedWord(MIME_EncodedWordEncoding.B,Encoding.UTF8),Encoding.UTF8)); tmpFs.Write(fieldBytes,0,fieldBytes.Length); } } // All header fetches must include header terminator(CRLF). if(tmpFs.Length >0 ){ tmpFs.WriteByte((byte)'\r'); tmpFs.WriteByte((byte)'\n'); } tmpFs.Position = 0; } #endregion #region MIME else if(string.Equals(partSpecifier,"MIME",StringComparison.InvariantCultureIgnoreCase)){ entity.Header.ToStream(tmpFs,new MIME_Encoding_EncodedWord(MIME_EncodedWordEncoding.B,Encoding.UTF8),Encoding.UTF8); // All header fetches must include header terminator(CRLF). if(tmpFs.Length >0 ){ tmpFs.WriteByte((byte)'\r'); tmpFs.WriteByte((byte)'\n'); } tmpFs.Position = 0; } #endregion #region TEXT else if(string.Equals(partSpecifier,"TEXT",StringComparison.InvariantCultureIgnoreCase)){ entity.Body.ToStream(tmpFs,new MIME_Encoding_EncodedWord(MIME_EncodedWordEncoding.B,Encoding.UTF8),Encoding.UTF8,false); tmpFs.Position = 0; } #endregion #region part-number only else{ entity.Body.ToStream(tmpFs,new MIME_Encoding_EncodedWord(MIME_EncodedWordEncoding.B,Encoding.UTF8),Encoding.UTF8,false); tmpFs.Position = 0; } #endregion } } #region Send data // All data wanted. if(offset < 0){ reponseBuffer.Append("BODY[" + section + "] {" + tmpFs.Length + "}\r\n"); WriteLine(reponseBuffer.ToString()); reponseBuffer = new StringBuilder(); this.TcpStream.WriteStream(tmpFs); LogAddWrite(tmpFs.Length,"Wrote " + tmpFs.Length + " bytes."); } // Partial data wanted. else{ // Offet out of range. if(offset >= tmpFs.Length){ reponseBuffer.Append("BODY[" + section + "]<" + offset + "> \"\""); } else{ tmpFs.Position = offset; int count = maxCount > -1 ? (int)Math.Min(maxCount,tmpFs.Length - tmpFs.Position) : (int)(tmpFs.Length - tmpFs.Position); reponseBuffer.Append("BODY[" + section + "]<" + offset + "> {" + count + "}"); WriteLine(reponseBuffer.ToString()); reponseBuffer = new StringBuilder(); this.TcpStream.WriteStream(tmpFs,count); LogAddWrite(tmpFs.Length,"Wrote " + count + " bytes."); } } #endregion } // Set Seen flag. if(!m_pSelectedFolder.IsReadOnly && dataItem is IMAP_t_Fetch_i_Body){ try{ OnStore(e.MessageInfo,IMAP_Flags_SetType.Add,new string[]{"Seen"},new IMAP_r_ServerStatus("dummy","OK","This is FETCH set Seen flag, this response not used.")); } catch{ } } } #endregion #region BODYSTRUCTURE else if(dataItem is IMAP_t_Fetch_i_BodyStructure){ reponseBuffer.Append(ConstructBodyStructure(message,true)); } #endregion #region ENVELOPE else if(dataItem is IMAP_t_Fetch_i_Envelope){ reponseBuffer.Append(IMAP_t_Fetch_r_i_Envelope.ConstructEnvelope(message)); } #endregion #region FLAGS else if(dataItem is IMAP_t_Fetch_i_Flags){ reponseBuffer.Append("FLAGS " + e.MessageInfo.FlagsToImapString()); } #endregion #region INTERNALDATE else if(dataItem is IMAP_t_Fetch_i_InternalDate){ reponseBuffer.Append("INTERNALDATE \"" + IMAP_Utils.DateTimeToString(e.MessageInfo.InternalDate) + "\""); } #endregion #region RFC822 else if(dataItem is IMAP_t_Fetch_i_Rfc822){ using(MemoryStreamEx tmpFs = new MemoryStreamEx(32000)){ message.ToStream(tmpFs,new MIME_Encoding_EncodedWord(MIME_EncodedWordEncoding.B,Encoding.UTF8),Encoding.UTF8); tmpFs.Position = 0; reponseBuffer.Append("RFC822 {" + tmpFs.Length + "}\r\n"); WriteLine(reponseBuffer.ToString()); reponseBuffer = new StringBuilder(); this.TcpStream.WriteStream(tmpFs); LogAddWrite(tmpFs.Length,"Wrote " + tmpFs.Length + " bytes."); } } #endregion #region RFC822.HEADER else if(dataItem is IMAP_t_Fetch_i_Rfc822Header){ MemoryStream ms = new MemoryStream(); message.Header.ToStream(ms,new MIME_Encoding_EncodedWord(MIME_EncodedWordEncoding.B,Encoding.UTF8),Encoding.UTF8); ms.Position = 0; reponseBuffer.Append("RFC822.HEADER {" + ms.Length + "}\r\n"); WriteLine(reponseBuffer.ToString()); reponseBuffer = new StringBuilder(); this.TcpStream.WriteStream(ms); LogAddWrite(ms.Length,"Wrote " + ms.Length + " bytes."); } #endregion #region RFC822.SIZE else if(dataItem is IMAP_t_Fetch_i_Rfc822Size){ reponseBuffer.Append("RFC822.SIZE " + e.MessageInfo.Size); } #endregion #region RFC822.TEXT else if(dataItem is IMAP_t_Fetch_i_Rfc822Text){ using(MemoryStreamEx tmpFs = new MemoryStreamEx(32000)){ message.Body.ToStream(tmpFs,new MIME_Encoding_EncodedWord(MIME_EncodedWordEncoding.B,Encoding.UTF8),Encoding.UTF8,false); tmpFs.Position = 0; reponseBuffer.Append("RFC822.TEXT {" + tmpFs.Length + "}\r\n"); WriteLine(reponseBuffer.ToString()); reponseBuffer = new StringBuilder(); this.TcpStream.WriteStream(tmpFs); LogAddWrite(tmpFs.Length,"Wrote " + tmpFs.Length + " bytes."); } } #endregion #region UID else if(dataItem is IMAP_t_Fetch_i_Uid){ reponseBuffer.Append("UID " + e.MessageInfo.UID); } #endregion } reponseBuffer.Append(")\r\n"); WriteLine(reponseBuffer.ToString()); }); // We have all needed data in message info. if(!msgDataNeeded){ foreach(IMAP_MessageInfo msgInfo in m_pSelectedFolder.Filter(uid,seqSet)){ fetchEArgs.AddData(msgInfo); } } // Request messages data. else{ OnFetch(fetchEArgs); } WriteLine(fetchEArgs.Response.ToString().Replace("%exectime",((DateTime.Now.Ticks - startTime) / (decimal)10000000).ToString("f2"))); }
/// <summary> /// Clones mail message. /// </summary> /// <returns>Returns cloned message.</returns> /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception> public Mail_Message Clone() { if(this.IsDisposed){ throw new ObjectDisposedException(this.GetType().Name); } MemoryStreamEx ms = new MemoryStreamEx(64000); this.ToStream(ms,null,null); ms.Position = 0; return Mail_Message.ParseFromStream(ms); }
/// <summary> /// Gets attachment stream. /// </summary> /// <returns>Returns attachment stream.</returns> internal Stream GetStream() { if(m_pStream == null){ m_pStream = File.OpenRead(m_FileName); } if(m_ZipCompress){ MemoryStreamEx retVal = new MemoryStreamEx(); using(ZipArchive archive = new ZipArchive(retVal,ZipArchiveMode.Create)){ ZipArchiveEntry entry = archive.CreateEntry(m_Name,CompressionLevel.Optimal); using(Stream zipStream = entry.Open()){ Net_Utils.StreamCopy(m_pStream,zipStream,64000); } } retVal.Position = 0; CloseStream(); return retVal; } return m_pStream; }
private void NLST(string argsText) { if(m_SessionRejected){ WriteLine("500 Bad sequence of commands: Session rejected."); return; } if(!this.IsAuthenticated){ WriteLine("530 Please authenticate firtst !"); return; } /* This command causes a directory listing to be sent from server to user site. The pathname should specify a directory or other system-specific file group descriptor; a null argument implies the current directory. The server will return a stream of names of files and no other information. The data will be transferred in ASCII or EBCDIC type over the data connection as valid pathname strings separated by <CRLF> or <NL>. (Again the user must ensure that the TYPE is correct.) This command is intended to return information that can be used by a program to further process the files automatically. For example, in the implementation of a "multiple get" function. */ FTP_e_GetDirListing eArgs = new FTP_e_GetDirListing(argsText); OnGetDirListing(eArgs); // Error getting directory listing. if(eArgs.Error != null){ foreach(FTP_t_ReplyLine reply in eArgs.Error){ WriteLine(reply.ToString()); } } // Listing succeeded. else{ // Build directory listing. MemoryStreamEx retVal = new MemoryStreamEx(8000); foreach(FTP_ListItem item in eArgs.Items){ byte[] data = Encoding.UTF8.GetBytes(item.Name + "\r\n"); retVal.Write(data,0,data.Length); } retVal.Position = 0; m_pDataConnection = new DataConnection(this,retVal,false); m_pDataConnection.Start(); } }
private void LIST(string argsText) { if(m_SessionRejected){ WriteLine("500 Bad sequence of commands: Session rejected."); return; } if(!this.IsAuthenticated){ WriteLine("530 Please authenticate firtst !"); return; } /* This command causes a list to be sent from the server to the passive DTP. If the pathname specifies a directory or other group of files, the server should transfer a list of files in the specified directory. If the pathname specifies a file then the server should send current information on the file. A null argument implies the user's current working or default directory. The data transfer is over the data connection in type ASCII or type EBCDIC. (The user must ensure that the TYPE is appropriately ASCII or EBCDIC). Since the information on a file may vary widely from system to system, this information may be hard to use automatically in a program, but may be quite useful to a human user. */ FTP_e_GetDirListing eArgs = new FTP_e_GetDirListing(argsText); OnGetDirListing(eArgs); // Error getting directory listing. if(eArgs.Error != null){ foreach(FTP_t_ReplyLine reply in eArgs.Error){ WriteLine(reply.ToString()); } } // Listing succeeded. else{ // Build directory listing. MemoryStreamEx retVal = new MemoryStreamEx(8000); foreach(FTP_ListItem item in eArgs.Items){ if(item.IsDir){ byte[] data = Encoding.UTF8.GetBytes(item.Modified.ToString("MM-dd-yy HH:mm") + " <DIR> " + item.Name + "\r\n"); retVal.Write(data,0,data.Length); } else{ byte[] data = Encoding.UTF8.GetBytes(item.Modified.ToString("MM-dd-yy HH:mm") + " " + item.Size.ToString() + " " + item.Name + "\r\n"); retVal.Write(data,0,data.Length); } } retVal.Position = 0; m_pDataConnection = new DataConnection(this,retVal,false); m_pDataConnection.Start(); } }
private void StoreUserFolderMessage(string argsText) { /* C: StoreUserFolderMessage <virtualServerID> "<folderOwnerUser>" "<folder>" <sizeInBytes> S: +OK Send message data C: <messageData> S: +OK Responses: +OK -ERR <errorText> */ try{ string[] args = TextUtils.SplitQuotedString(argsText,' ',true); if(args.Length != 4){ WriteLine("-ERR Invalid arguments. Syntax: StoreUserFolderMessage <virtualServerID> \"<folderOwnerUser>\" \"<folder>\" <sizeInBytes>"); return; } foreach(VirtualServer virtualServer in this.Server.MailServer.VirtualServers){ if(virtualServer.ID.ToLower() == args[0].ToLower()){ // TODO: handle no existent user // TODO: handle no existent folder WriteLine("+OK"); using(MemoryStreamEx messageStream = new MemoryStreamEx(32000)){ this.TcpStream.ReadFixedCount(messageStream,Convert.ToInt32(args[3])); messageStream.Position = 0; virtualServer.API.StoreMessage( "system", args[1], args[2], messageStream, DateTime.Now, new string[]{"Recent"} ); } WriteLine("+OK"); return; } } WriteLine("-ERR Specified virtual server with ID '" + args[0] + "' doesn't exist !"); } catch(Exception x){ WriteLine("-ERR " + x.Message); } }