예제 #1
0
        /// <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();
        }