/// <summary> /// Fetches specifes messages specified fetch items. /// </summary> /// <param name="sequence_set">IMAP sequence-set.</param> /// <param name="fetchFlags">Specifies what data to fetch from IMAP server.</param> /// <param name="setSeenFlag">If true message seen flag is setted.</param> /// <param name="uidFetch">Specifies if sequence-set contains message UIDs or message numbers.</param> /// <returns>Returns requested fetch items.</returns> /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception> /// <exception cref="InvalidOperationException">Is raised when IMAP client is not connected,not authenticated and folder not selected.</exception> public IMAP_FetchItem[] FetchMessages(IMAP_SequenceSet sequence_set, IMAP_FetchItem_Flags fetchFlags, bool setSeenFlag, bool uidFetch) { if (this.IsDisposed) { throw new ObjectDisposedException(this.GetType().Name); } if (!this.IsConnected) { throw new InvalidOperationException("You must connect first."); } if (!this.IsAuthenticated) { throw new InvalidOperationException("The command is only valid in authenticated state."); } if (m_SelectedFolder.Length == 0) { throw new InvalidOperationException("The command is only valid in selected state."); } List<IMAP_FetchItem> fetchItems = new List<IMAP_FetchItem>(); //--- Construct FETCH command line -----------------------------------------------------------------------// string fetchCmdLine = GetNextCmdTag(); if (uidFetch) { fetchCmdLine += " UID"; } fetchCmdLine += " FETCH " + sequence_set.ToSequenceSetString() + " (UID"; // FLAGS if ((fetchFlags & IMAP_FetchItem_Flags.MessageFlags) != 0) { fetchCmdLine += " FLAGS"; } // RFC822.SIZE if ((fetchFlags & IMAP_FetchItem_Flags.Size) != 0) { fetchCmdLine += " RFC822.SIZE"; } // INTERNALDATE if ((fetchFlags & IMAP_FetchItem_Flags.InternalDate) != 0) { fetchCmdLine += " INTERNALDATE"; } // ENVELOPE if ((fetchFlags & IMAP_FetchItem_Flags.Envelope) != 0) { fetchCmdLine += " ENVELOPE"; } // BODYSTRUCTURE if ((fetchFlags & IMAP_FetchItem_Flags.BodyStructure) != 0) { fetchCmdLine += " BODYSTRUCTURE"; } // BODY[] or BODY.PEEK[] if ((fetchFlags & IMAP_FetchItem_Flags.Message) != 0) { if (setSeenFlag) { fetchCmdLine += " BODY[]"; } else { fetchCmdLine += " BODY.PEEK[]"; } } // BODY[HEADER] or BODY.PEEK[HEADER] ---> This needed only if full message isn't requested. if ((fetchFlags & IMAP_FetchItem_Flags.Message) == 0 && (fetchFlags & IMAP_FetchItem_Flags.Header) != 0) { if (setSeenFlag) { fetchCmdLine += " BODY[HEADER]"; } else { fetchCmdLine += " BODY.PEEK[HEADER]"; } } //--------------------------------------------------------------------------------------------------------// fetchCmdLine += ")"; // Send fetch command line to server. int countWritten = this.TcpStream.WriteLine(fetchCmdLine); LogAddWrite(countWritten, fetchCmdLine); // Read un-tagged response lines while we get final response line. byte[] lineBuffer = new byte[100000]; string line = ""; while (true) { SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(lineBuffer, SizeExceededAction.JunkAndThrowException); this.TcpStream.ReadLine(args, false); if (args.Error != null) { throw args.Error; } line = args.LineUtf8; LogAddRead(args.BytesInBuffer, line); // We have un-tagged resposne. if (line.StartsWith("*")) { if (IsStatusResponse(line)) { ProcessStatusResponse(line); } else { int no = 0; int uid = 0; int size = 0; byte[] data = null; IMAP_MessageFlags flags = IMAP_MessageFlags.Recent; string envelope = ""; string bodystructure = ""; string internalDate = ""; // Remove * line = RemoveCmdTag(line); // Get message number no = Convert.ToInt32(line.Substring(0, line.IndexOf(" "))); // Get rid of FETCH and parse params. Reply:* 1 FETCH (UID 12 BODY[] ...) line = line.Substring(line.IndexOf("FETCH (") + 7); StringReader r = new StringReader(line); // Loop fetch result fields while (r.Available > 0) { r.ReadToFirstChar(); // Fetch command closing ) parenthesis if (r.SourceString == ")") { break; } #region UID <value> // UID <value> else if (r.StartsWith("UID", false)) { // Remove UID word from reply r.ReadSpecifiedLength("UID".Length); r.ReadToFirstChar(); // Read <value> string word = r.ReadWord(); if (word == null) { throw new Exception("IMAP server didn't return UID <value> !"); } else { uid = Convert.ToInt32(word); } } #endregion #region RFC822.SIZE <value> // RFC822.SIZE <value> else if (r.StartsWith("RFC822.SIZE", false)) { // Remove RFC822.SIZE word from reply r.ReadSpecifiedLength("RFC822.SIZE".Length); r.ReadToFirstChar(); // Read <value> string word = r.ReadWord(); if (word == null) { throw new Exception("IMAP server didn't return RFC822.SIZE <value> !"); } else { try { size = Convert.ToInt32(word); } catch { throw new Exception("IMAP server returned invalid RFC822.SIZE <value> '" + word + "' !"); } } } #endregion #region INTERNALDATE <value> // INTERNALDATE <value> else if (r.StartsWith("INTERNALDATE", false)) { // Remove INTERNALDATE word from reply r.ReadSpecifiedLength("INTERNALDATE".Length); r.ReadToFirstChar(); // Read <value> string word = r.ReadWord(); if (word == null) { throw new Exception("IMAP server didn't return INTERNALDATE <value> !"); } else { internalDate = word; } } #endregion #region ENVELOPE (<envelope-string>) else if (r.StartsWith("ENVELOPE", false)) { // Remove ENVELOPE word from reply r.ReadSpecifiedLength("ENVELOPE".Length); r.ReadToFirstChar(); /* Handle string literals {count-to-read}<CRLF>data(length = count-to-read). (string can be quoted string or literal) Loop while get envelope,invalid response or timeout. */ while (true) { try { envelope = r.ReadParenthesized(); break; } catch (Exception x) { string s = r.ReadToEnd(); /* partial_envelope {count-to-read} Example: ENVELOPE ("Mon, 03 Apr 2006 10:10:10 GMT" {35} */ if (s.EndsWith("}")) { // Get partial envelope and append it back to reader r.AppenString(s.Substring(0, s.LastIndexOf('{'))); // Read remaining envelope and append it to reader. int countToRead = Convert.ToInt32(s.Substring(s.LastIndexOf('{') + 1, s.LastIndexOf('}') - s.LastIndexOf('{') - 1)); string reply = this.TcpStream.ReadFixedCountString(countToRead); LogAddRead(countToRead, reply); r.AppenString(TextUtils.QuoteString(reply)); // Read fetch continuing line. this.TcpStream.ReadLine(args, false); if (args.Error != null) { throw args.Error; } line = args.LineUtf8; LogAddRead(args.BytesInBuffer, line); r.AppenString(line); } // Unexpected response else { throw x; } } } } #endregion #region BODYSTRUCTURE (<bodystructure-string>) else if (r.StartsWith("BODYSTRUCTURE", false)) { // Remove BODYSTRUCTURE word from reply r.ReadSpecifiedLength("BODYSTRUCTURE".Length); r.ReadToFirstChar(); bodystructure = r.ReadParenthesized(); } #endregion #region BODY[] or BODY[HEADER] // BODY[] or BODY[HEADER] else if (r.StartsWith("BODY", false)) { if (r.StartsWith("BODY[]", false)) { // Remove BODY[] r.ReadSpecifiedLength("BODY[]".Length); } else if (r.StartsWith("BODY[HEADER]", false)) { // Remove BODY[HEADER] r.ReadSpecifiedLength("BODY[HEADER]".Length); } else { throw new Exception("Invalid FETCH response: " + r.SourceString); } r.ReadToFirstChar(); // We must now have {<size-to-read>}, or there is error if (!r.StartsWith("{")) { throw new Exception("Invalid FETCH BODY[] or BODY[HEADER] response: " + r.SourceString); } // Read <size-to-read> int dataLength = Convert.ToInt32(r.ReadParenthesized()); // Read data MemoryStream storeStrm = new MemoryStream(dataLength); this.TcpStream.ReadFixedCount(storeStrm, dataLength); LogAddRead(dataLength, "Readed " + dataLength.ToString() + " bytes."); data = storeStrm.ToArray(); // Read fetch continuing line. this.TcpStream.ReadLine(args, false); if (args.Error != null) { throw args.Error; } line = args.LineUtf8; LogAddRead(args.BytesInBuffer, line); r.AppenString(line); } #endregion #region FLAGS (<flags-list>) // FLAGS (<flags-list>) else if (r.StartsWith("FLAGS", false)) { // Remove FLAGS word from reply r.ReadSpecifiedLength("FLAGS".Length); r.ReadToFirstChar(); // Read (<flags-list>) string flagsList = r.ReadParenthesized(); if (flagsList == null) { throw new Exception("IMAP server didn't return FLAGS (<flags-list>) !"); } else { flags = IMAP_Utils.ParseMessageFlags(flagsList); } } #endregion else { throw new Exception("Not supported fetch reply: " + r.SourceString); } } fetchItems.Add(new IMAP_FetchItem(no, uid, size, data, flags, internalDate, envelope, bodystructure, fetchFlags)); } } else { break; } } if (!RemoveCmdTag(line).ToUpper().StartsWith("OK")) { throw new IMAP_ClientException(line); } return fetchItems.ToArray(); }
/// <summary> /// Fetches specifes messages specified fetch items. /// </summary> /// <param name="sequence_set">IMAP sequence-set.</param> /// <param name="fetchFlags">Specifies what data to fetch from IMAP server.</param> /// <param name="setSeenFlag">If true message seen flag is setted.</param> /// <param name="uidFetch">Specifies if sequence-set contains message UIDs or message numbers.</param> /// <returns></returns> public IMAP_FetchItem[] FetchMessages(IMAP_SequenceSet sequence_set,IMAP_FetchItem_Flags fetchFlags,bool setSeenFlag,bool uidFetch) { if(!m_Connected){ throw new Exception("You must connect first !"); } if(!m_Authenticated){ throw new Exception("You must authenticate first !"); } if(m_SelectedFolder.Length == 0){ throw new Exception("You must select folder first !"); } List<IMAP_FetchItem> fetchItems = new List<IMAP_FetchItem>(); //--- Construct FETCH command line -----------------------------------------------------------------------// string fetchCmdLine = "a1"; if(uidFetch){ fetchCmdLine += " UID"; } fetchCmdLine += " FETCH " + sequence_set.ToSequenceSetString() + " (UID"; // FLAGS if((fetchFlags & IMAP_FetchItem_Flags.MessageFlags) != 0){ fetchCmdLine += " FLAGS"; } // RFC822.SIZE if((fetchFlags & IMAP_FetchItem_Flags.Size) != 0){ fetchCmdLine += " RFC822.SIZE"; } // INTERNALDATE if((fetchFlags & IMAP_FetchItem_Flags.InternalDate) != 0){ fetchCmdLine += " INTERNALDATE"; } // ENVELOPE if((fetchFlags & IMAP_FetchItem_Flags.Envelope) != 0){ fetchCmdLine += " ENVELOPE"; } // BODYSTRUCTURE if((fetchFlags & IMAP_FetchItem_Flags.BodyStructure) != 0){ fetchCmdLine += " BODYSTRUCTURE"; } // BODY[] or BODY.PEEK[] if((fetchFlags & IMAP_FetchItem_Flags.Message) != 0){ if(setSeenFlag){ fetchCmdLine += " BODY[]"; } else{ fetchCmdLine += " BODY.PEEK[]"; } } // BODY[HEADER] or BODY.PEEK[HEADER] ---> This needed only if full message isn't requested. if((fetchFlags & IMAP_FetchItem_Flags.Message) == 0 && (fetchFlags & IMAP_FetchItem_Flags.Header) != 0){ if(setSeenFlag){ fetchCmdLine += " BODY[HEADER]"; } else{ fetchCmdLine += " BODY.PEEK[HEADER]"; } } //--------------------------------------------------------------------------------------------------------// fetchCmdLine += ")"; // Send fetch command line to server m_pSocket.WriteLine(fetchCmdLine); // Must get lines with * and cmdTag + OK or cmdTag BAD/NO string reply = m_pSocket.ReadLine(50000); // Read multiline response while(reply.StartsWith("*")){ // Fetch may return status response there, skip them if(IsStatusResponse(reply)){ // Read next fetch item or server response reply = m_pSocket.ReadLine(50000); continue; } int no = 0; int uid = 0; int size = 0; byte[] data = null; IMAP_MessageFlags flags = IMAP_MessageFlags.Recent; string envelope = ""; string bodystructure = ""; string internalDate = ""; // Remove * reply = reply.Substring(1).TrimStart(); // Get message number no = Convert.ToInt32(reply.Substring(0,reply.IndexOf(" "))); // Get rid of FETCH and parse params. Reply:* 1 FETCH (UID 12 BODY[] ...) reply = reply.Substring(reply.IndexOf("FETCH (") + 7); StringReader r = new StringReader(reply); // Loop fetch result fields while(r.Available > 0){ r.ReadToFirstChar(); // Fetch command closing ) parenthesis if(r.SourceString == ")"){ break; } #region UID <value> // UID <value> else if(r.StartsWith("UID",false)){ // Remove UID word from reply r.ReadSpecifiedLength("UID".Length); r.ReadToFirstChar(); // Read <value> string word = r.ReadWord(); if(word == null){ throw new Exception("IMAP server didn't return UID <value> !"); } else{ uid = Convert.ToInt32(word); } } #endregion #region RFC822.SIZE <value> // RFC822.SIZE <value> else if(r.StartsWith("RFC822.SIZE",false)){ // Remove RFC822.SIZE word from reply r.ReadSpecifiedLength("RFC822.SIZE".Length); r.ReadToFirstChar(); // Read <value> string word = r.ReadWord(); if(word == null){ throw new Exception("IMAP server didn't return RFC822.SIZE <value> !"); } else{ try{ size = Convert.ToInt32(word); } catch{ throw new Exception("IMAP server returned invalid RFC822.SIZE <value> '" + word + "' !"); } } } #endregion #region INTERNALDATE <value> // INTERNALDATE <value> else if(r.StartsWith("INTERNALDATE",false)){ // Remove INTERNALDATE word from reply r.ReadSpecifiedLength("INTERNALDATE".Length); r.ReadToFirstChar(); // Read <value> string word = r.ReadWord(); if(word == null){ throw new Exception("IMAP server didn't return INTERNALDATE <value> !"); } else{ internalDate = word; } } #endregion #region ENVELOPE (<envelope-string>) else if(r.StartsWith("ENVELOPE",false)){ // Remove ENVELOPE word from reply r.ReadSpecifiedLength("ENVELOPE".Length); r.ReadToFirstChar(); /* Handle string literals {count-to-read}<CRLF>data(length = count-to-read). (string can be quoted string or literal) Loop while get envelope,invalid response or timeout. */ while(true){ try{ envelope = r.ReadParenthesized(); break; } catch(Exception x){ string s = r.ReadToEnd(); /* partial_envelope {count-to-read} Example: ENVELOPE ("Mon, 03 Apr 2006 10:10:10 GMT" {35} */ if(s.EndsWith("}")){ // Get partial envelope and append it back to reader r.AppenString(s.Substring(0,s.LastIndexOf('{'))); // Read remaining envelope and append it to reader int countToRead = Convert.ToInt32(s.Substring(s.LastIndexOf('{') + 1,s.LastIndexOf('}') - s.LastIndexOf('{') - 1)); MemoryStream strm = new MemoryStream(); m_pSocket.ReadSpecifiedLength(countToRead,strm); r.AppenString(TextUtils.QuoteString(System.Text.Encoding.Default.GetString(strm.ToArray()))); // Read fetch continuing line r.AppenString(m_pSocket.ReadLine(50000)); } // Unexpected response else{ throw x; } } } } #endregion #region BODYSTRUCTURE (<bodystructure-string>) else if(r.StartsWith("BODYSTRUCTURE",false)){ // Remove BODYSTRUCTURE word from reply r.ReadSpecifiedLength("BODYSTRUCTURE".Length); r.ReadToFirstChar(); bodystructure = r.ReadParenthesized(); } #endregion #region BODY[] or BODY[HEADER] // BODY[] or BODY[HEADER] else if(r.StartsWith("BODY",false)){ if(r.StartsWith("BODY[]",false)){ // Remove BODY[] r.ReadSpecifiedLength("BODY[]".Length); } else if(r.StartsWith("BODY[HEADER]",false)){ // Remove BODY[HEADER] r.ReadSpecifiedLength("BODY[HEADER]".Length); } else{ throw new Exception("Invalid FETCH response: " + r.SourceString); } r.ReadToFirstChar(); // We must now have {<size-to-read>}, or there is error if(!r.StartsWith("{")){ throw new Exception("Invalid FETCH BODY[] or BODY[HEADER] response: " + r.SourceString); } // Read <size-to-read> int dataLength = Convert.ToInt32(r.ReadParenthesized()); // Read data MemoryStream storeStrm = new MemoryStream(dataLength); m_pSocket.ReadSpecifiedLength(dataLength,storeStrm); data = storeStrm.ToArray(); // Read fetch continuing line r.AppenString(m_pSocket.ReadLine(50000).Trim()); } #endregion #region FLAGS (<flags-list>) // FLAGS (<flags-list>) else if(r.StartsWith("FLAGS",false)){ // Remove FLAGS word from reply r.ReadSpecifiedLength("FLAGS".Length); r.ReadToFirstChar(); // Read (<flags-list>) string flagsList = r.ReadParenthesized(); if(flagsList == null){ throw new Exception("IMAP server didn't return FLAGS (<flags-list>) !"); } else{ flags = IMAP_Utils.ParseMessageFlags(flagsList); } } #endregion else{ throw new Exception("Not supported fetch reply: " + r.SourceString); } } fetchItems.Add(new IMAP_FetchItem(no,uid,size,data,flags,internalDate,envelope,bodystructure,fetchFlags)); // Read next fetch item or server response reply = m_pSocket.ReadLine(50000); } // We must get OK or otherwise there is error if(!RemoveCmdTag(reply).ToUpper().StartsWith("OK")){ if(!reply.ToUpper().StartsWith("NO")){ throw new Exception("Server returned:" + reply); } } return fetchItems.ToArray(); }