コード例 #1
0
        /// <summary>
        /// Returns parsed IMAP SEARCH <b>AND</b> key group.
        /// </summary>
        /// <param name="r">String reader.</param>
        /// <returns>Returns parsed IMAP SEARCH <b>AND</b> key group.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>r</b> is null reference.</exception>
        /// <exception cref="ParseException">Is raised when parsing fails.</exception>
        public static IMAP_Search_Key_Group Parse(StringReader r)
        {
            if(r == null){
                throw new ArgumentNullException("r");
            }

            // Remove parenthesis, if any.
            if(r.StartsWith("(")){
                r = new StringReader(r.ReadParenthesized());
            }            

            IMAP_Search_Key_Group retVal = new IMAP_Search_Key_Group();

            r.ReadToFirstChar();
            while(r.Available > 0){
                retVal.m_pKeys.Add(IMAP_Search_Key.ParseKey(r));
            }

            return retVal;
        }
コード例 #2
0
ファイル: IMAP_Search_Key.cs プロジェクト: nbhopson/QMail
        /// <summary>
        /// Parses one search key or search key group.
        /// </summary>
        /// <param name="r">String reader.</param>
        /// <returns>Returns one parsed search key or search key group.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>r</b> is null reference.</exception>
        /// <exception cref="ParseException">Is raised when parsing fails.</exception>
        internal static IMAP_Search_Key ParseKey(StringReader r)
        {
            if(r == null){
                throw new ArgumentNullException("r");
            }

            r.ReadToFirstChar();

            // Keys group
            if(r.StartsWith("(",false)){
                return IMAP_Search_Key_Group.Parse(new StringReader(r.ReadParenthesized()));
            }
            // ANSWERED
            else if(r.StartsWith("ANSWERED",false)){
                return IMAP_Search_Key_Answered.Parse(r);
            }
            // BCC
            else if(r.StartsWith("BCC",false)){
                return IMAP_Search_Key_Bcc.Parse(r);
            }
            // BEFORE
            else if(r.StartsWith("BEFORE",false)){
                return IMAP_Search_Key_Before.Parse(r);
            }
            // BODY
            else if(r.StartsWith("BODY",false)){
                return IMAP_Search_Key_Body.Parse(r);
            }
            // CC
            else if(r.StartsWith("CC",false)){
                return IMAP_Search_Key_Cc.Parse(r);
            }
            // DELETED
            else if(r.StartsWith("DELETED",false)){
                return IMAP_Search_Key_Deleted.Parse(r);
            }
            // DRAFT
            else if(r.StartsWith("DRAFT",false)){
                return IMAP_Search_Key_Draft.Parse(r);
            }
            // FLAGGED
            else if(r.StartsWith("FLAGGED",false)){
                return IMAP_Search_Key_Flagged.Parse(r);
            }
            // FROM
            else if(r.StartsWith("FROM",false)){
                return IMAP_Search_Key_From.Parse(r);
            }
            // HEADER
            else if(r.StartsWith("HEADER",false)){
                return IMAP_Search_Key_Header.Parse(r);
            }
            // KEYWORD
            else if(r.StartsWith("KEYWORD",false)){
                return IMAP_Search_Key_Keyword.Parse(r);
            }
            // LARGER
            else if(r.StartsWith("LARGER",false)){
                return IMAP_Search_Key_Larger.Parse(r);
            }
            // NEW
            else if(r.StartsWith("NEW",false)){
                return IMAP_Search_Key_New.Parse(r);
            }
            // NOT
            else if(r.StartsWith("NOT",false)){
                return IMAP_Search_Key_Not.Parse(r);
            }
            // OLD
            else if(r.StartsWith("OLD",false)){
                return IMAP_Search_Key_Old.Parse(r);
            }
            // ON
            else if(r.StartsWith("ON",false)){
                return IMAP_Search_Key_On.Parse(r);
            }
            // OR
            else if(r.StartsWith("OR",false)){
                return IMAP_Search_Key_Or.Parse(r);
            }
            // RECENT
            else if(r.StartsWith("RECENT",false)){
                return IMAP_Search_Key_Recent.Parse(r);
            }
            // SEEN
            else if(r.StartsWith("SEEN",false)){
                return IMAP_Search_Key_Seen.Parse(r);
            }
            // SENTBEFORE
            else if(r.StartsWith("SENTBEFORE",false)){
                return IMAP_Search_Key_SentBefore.Parse(r);
            }
            // SENTON
            else if(r.StartsWith("SENTON",false)){
                return IMAP_Search_Key_SentOn.Parse(r);
            }
            // SENTSINCE
            else if(r.StartsWith("SENTSINCE",false)){
                return IMAP_Search_Key_SentSince.Parse(r);
            }
            // SEQSET
            else if(r.StartsWith("SEQSET",false)){
                return IMAP_Search_Key_SeqSet.Parse(r);
            }
            // SINCE
            else if(r.StartsWith("SINCE",false)){
                return IMAP_Search_Key_Since.Parse(r);
            }
            // SMALLER
            else if(r.StartsWith("SMALLER",false)){
                return IMAP_Search_Key_Smaller.Parse(r);
            }
            // SUBJECT
            else if(r.StartsWith("SUBJECT",false)){
                return IMAP_Search_Key_Subject.Parse(r);
            }
            // TEXT
            else if(r.StartsWith("TEXT",false)){
                return IMAP_Search_Key_Text.Parse(r);
            }
            // TO
            else if(r.StartsWith("TO",false)){
                return IMAP_Search_Key_To.Parse(r);
            }
            // UID
            else if(r.StartsWith("UID",false)){
                return IMAP_Search_Key_Uid.Parse(r);
            }
            // UNANSWERED
            else if(r.StartsWith("UNANSWERED",false)){
                return IMAP_Search_Key_Unanswered.Parse(r);
            }
            // UNDELETED
            else if(r.StartsWith("UNDELETED",false)){
                return IMAP_Search_Key_Undeleted.Parse(r);
            }
            // UNDRAFT
            else if(r.StartsWith("UNDRAFT",false)){
                return IMAP_Search_Key_Undraft.Parse(r);
            }
            // UNFLAGGED
            else if(r.StartsWith("UNFLAGGED",false)){
                return IMAP_Search_Key_Unflagged.Parse(r);
            }
            // UNKEYWORD
            else if(r.StartsWith("UNKEYWORD",false)){
                return IMAP_Search_Key_Unkeyword.Parse(r);
            }
            // UNSEEN
            else if(r.StartsWith("UNSEEN",false)){
                return IMAP_Search_Key_Unseen.Parse(r);
            }
            else{
                // Check if we hae sequence-set. Because of IMAP specification sucks a little here, why the hell they didn't
                // do the keyword(SEQSET) for it, like UID. Now we just have to try if it is sequence-set or BAD key.
                try{
                   return IMAP_Search_Key_SeqSet.Parse(r);
                }
                catch{
                   throw new ParseException("Unknown search key '" + r.ReadToEnd() + "'.");
                }
            }
        }
コード例 #3
0
ファイル: IMAP_r_u_Fetch.cs プロジェクト: nbhopson/QMail
        /// <summary>
        /// Reads IMAP string(string-literal,quoted-string,NIL) and remaining FETCH line if needed.
        /// </summary>
        /// <param name="imap">IMAP client.</param>
        /// <param name="r">Fetch line reader.</param>
        /// <param name="callback">Fetch completion callback.</param>
        /// <param name="stream">Stream where to store readed data.</param>
        /// <returns>Returns true if completed asynchronously or false if completed synchronously.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>imap</b>,<b>r</b>,<b>callback</b> or <b>stream</b> is null reference.</exception>
        private bool ReadData(IMAP_Client imap,StringReader r,EventHandler<EventArgs<Exception>> callback,Stream stream)
        {
            if(imap == null){
                throw new ArgumentNullException("imap");
            }
            if(r == null){
                throw new ArgumentNullException("r");
            }
            if(callback == null){
                throw new ArgumentNullException("callback");
            }
            if(stream == null){
                throw new ArgumentNullException("stream");
            }

            r.ReadToFirstChar();

            // We don't have data.
            if(r.StartsWith("NIL",false)){
                // Eat NIL.
                r.ReadWord();

                return false;
            }
            // Data value is returned as string-literal.
            else if(r.StartsWith("{",false)){
                IMAP_Client.ReadStringLiteralAsyncOP op = new IMAP_Client.ReadStringLiteralAsyncOP(stream,Convert.ToInt32(r.ReadParenthesized()));
                op.CompletedAsync += delegate(object sender,EventArgs<IMAP_Client.ReadStringLiteralAsyncOP> e){
                    try{
                        // Read string literal failed.
                        if(op.Error != null){
                            callback(this,new EventArgs<Exception>(op.Error));
                        }
                        else{
                            // Read next fetch line completed synchronously.
                            if(!ReadNextFetchLine(imap,r,callback)){
                                ParseDataItems(imap,r,callback);
                            }
                        }
                    }
                    catch(Exception x){
                        callback(this,new EventArgs<Exception>(x));
                    }
                    finally{
                        op.Dispose();
                    }
                };

                // Read string literal completed sync.
                if(!imap.ReadStringLiteralAsync(op)){
                    try{
                        // Read string literal failed.
                        if(op.Error != null){
                            callback(this,new EventArgs<Exception>(op.Error));

                            return true;
                        }
                        else{
                            // Read next fetch line completed synchronously.
                            if(!ReadNextFetchLine(imap,r,callback)){
                                return false;
                            }
                            else{
                                return true;
                            }
                        }
                    }
                    finally{
                        op.Dispose();
                    }
                }
                // Read string literal completed async.
                else{
                    return true;
                }
            }
            // Data is quoted-string.
            else{
                byte[] data = Encoding.UTF8.GetBytes(r.ReadWord());
                stream.Write(data,0,data.Length);

                return false;
            }
        }
コード例 #4
0
ファイル: IMAP_r_u_Fetch.cs プロジェクト: nbhopson/QMail
        /// <summary>
        /// Starts parsing fetch data-items,
        /// </summary>
        /// <param name="imap">IMAP client.</param>
        /// <param name="r">Fetch line reader.</param>
        /// <param name="callback">Callback to be called when parsing completes.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>imap</b>,<b>r</b> or <b>callback</b> is null reference.</exception>
        private void ParseDataItems(IMAP_Client imap,StringReader r,EventHandler<EventArgs<Exception>> callback)
        {
            if(imap == null){
                throw new ArgumentNullException("imap");
            }
            if(r == null){
                throw new ArgumentNullException("r");
            }
            if(callback == null){
                throw new ArgumentNullException("callback");
            }

            /* RFC 3501 7.4.2. FETCH Response.
                Example:    S: * 23 FETCH (FLAGS (\Seen) RFC822.SIZE 44827)
            */

            while(true){
                r.ReadToFirstChar();

                #region BODY[]

                if(r.StartsWith("BODY[",false)){
                    /* RFC 3501 7.4.2. FETCH Response.
                        BODY[<section>]<<origin octet>>
                         A string expressing the body contents of the specified section.
                         The string SHOULD be interpreted by the client according to the
                         content transfer encoding, body type, and subtype.

                         If the origin octet is specified, this string is a substring of
                         the entire body contents, starting at that origin octet.  This
                         means that BODY[]<0> MAY be truncated, but BODY[] is NEVER
                         truncated.

                            Note: The origin octet facility MUST NOT be used by a server
                            in a FETCH response unless the client specifically requested
                            it by means of a FETCH of a BODY[<section>]<<partial>> data
                            item.

                         8-bit textual data is permitted if a [CHARSET] identifier is
                         part of the body parameter parenthesized list for this section.
                         Note that headers (part specifiers HEADER or MIME, or the
                         header portion of a MESSAGE/RFC822 part), MUST be 7-bit; 8-bit
                         characters are not permitted in headers.  Note also that the
                         [RFC-2822] delimiting blank line between the header and the
                         body is not affected by header line subsetting; the blank line
                         is always included as part of header data, except in the case
                         of a message which has no body and no blank line.

                         Non-textual data such as binary data MUST be transfer encoded
                         into a textual form, such as BASE64, prior to being sent to the
                         client.  To derive the original binary data, the client MUST
                         decode the transfer encoded string.
                    */

                    // Eat BODY word.
                    r.ReadWord();

                    // Read body-section.
                    string section = r.ReadParenthesized();

                    // Read origin if any.
                    int offset = -1;
                    if(r.StartsWith("<")){
                        offset = Convert.ToInt32(r.ReadParenthesized().Split(' ')[0]);
                    }

                    IMAP_t_Fetch_r_i_Body dataItem = new IMAP_t_Fetch_r_i_Body(section,offset,new MemoryStreamEx(32000));
                    m_pDataItems.Add(dataItem);

                    // Raise event, allow user to specify store stream.
                    IMAP_Client_e_FetchGetStoreStream eArgs = new IMAP_Client_e_FetchGetStoreStream(this,dataItem);
                    imap.OnFetchGetStoreStream(eArgs);
                    // User specified own stream, use it.
                    if(eArgs.Stream != null){
                        dataItem.Stream.Dispose();
                        dataItem.SetStream(eArgs.Stream);
                    }

                    // Read data will complete async and will continue data-items parsing, exit this method.
                    if(ReadData(imap,r,callback,dataItem.Stream)){
                        return;
                    }
                    // Continue processing.
                    //else{
                }

                #endregion

                #region BODY

                else if(r.StartsWith("BODY ",false)){
                    //IMAP_t_Fetch_r_i_BodyS
                }

                #endregion

                #region BODYSTRUCTURE

                else if(r.StartsWith("BODYSTRUCTURE",false)){
                    //IMAP_t_Fetch_r_i_BodyStructure
                }

                #endregion

                #region ENVELOPE

                else if(r.StartsWith("ENVELOPE",false)){
                    // Envelope can contain string literals, we just try to parse it.
                    // If parse fails, just get string literal and try again as long as all ENVELOPE data has read.

                    string envelope = null;
                    while(true){
                        // Create temporary reader(we don't want to read partial ENVELOPE data from reader).
                        StringReader tmpReader = new StringReader(r.SourceString);

                        // Eat ENVELOPE word.
                        tmpReader.ReadWord();
                        tmpReader.ReadToFirstChar();

                        try{
                            envelope = tmpReader.ReadParenthesized();
                            // We got full ENVELOPE, so use tmp reader as reader.
                            r = tmpReader;

                            break;
                        }
                        catch{
                            // Read completed async, it will continue parsing.
                            if(ReadStringLiteral(imap,r,callback)){
                                return;
                            }
                        }
                    }

                    m_pDataItems.Add(IMAP_t_Fetch_r_i_Envelope.Parse(new StringReader(envelope)));
                }

                #endregion

                #region FLAGS

                else if(r.StartsWith("FLAGS",false)){
                    /* RFC 3501 7.4.2. FETCH Response.
                        FLAGS
                            A parenthesized list of flags that are set for this message.
                    */

                    // Eat FLAGS word.
                    r.ReadWord();

                    m_pDataItems.Add(new IMAP_t_Fetch_r_i_Flags(IMAP_t_MsgFlags.Parse(r.ReadParenthesized())));
                }

                #endregion

                #region INTERNALDATE

                else if(r.StartsWith("INTERNALDATE",false)){
                    /* RFC 3501 7.4.2. FETCH Response.
                        INTERNALDATE
                            A string representing the internal date of the message.
                    */

                    // Eat INTERNALDATE word.
                    r.ReadWord();

                    m_pDataItems.Add(new IMAP_t_Fetch_r_i_InternalDate(IMAP_Utils.ParseDate(r.ReadWord())));
                }

                #endregion

                #region RFC822

                else if(r.StartsWith("RFC822 ",false)){
                    /* RFC 3501 7.4.2. FETCH Response.
                        RFC822
                            Equivalent to BODY[].
                    */

                    // Eat RFC822 word.
                    r.ReadWord();
                    r.ReadToFirstChar();

                    IMAP_t_Fetch_r_i_Rfc822 dataItem = new IMAP_t_Fetch_r_i_Rfc822(new MemoryStreamEx(32000));
                    m_pDataItems.Add(dataItem);

                    // Raise event, allow user to specify store stream.
                    IMAP_Client_e_FetchGetStoreStream eArgs = new IMAP_Client_e_FetchGetStoreStream(this,dataItem);
                    imap.OnFetchGetStoreStream(eArgs);
                    // User specified own stream, use it.
                    if(eArgs.Stream != null){
                        dataItem.Stream.Dispose();
                        dataItem.SetStream(eArgs.Stream);
                    }

                    // Read data will complete async and will continue data-items parsing, exit this method.
                    if(ReadData(imap,r,callback,dataItem.Stream)){
                        return;
                    }
                    // Continue processing.
                    //else{
                }

                #endregion

                #region RFC822.HEADER

                else if(r.StartsWith("RFC822.HEADER",false)){
                    /* RFC 3501 7.4.2. FETCH Response.
                        RFC822.HEADER
                            Equivalent to BODY[HEADER].  Note that this did not result in
                            \Seen being set, because RFC822.HEADER response data occurs as
                            a result of a FETCH of RFC822.HEADER.  BODY[HEADER] response
                            data occurs as a result of a FETCH of BODY[HEADER] (which sets
                            \Seen) or BODY.PEEK[HEADER] (which does not set \Seen).
                    */

                    // Eat RFC822.HEADER word.
                    r.ReadWord();
                    r.ReadToFirstChar();

                    IMAP_t_Fetch_r_i_Rfc822Header dataItem = new IMAP_t_Fetch_r_i_Rfc822Header(new MemoryStreamEx(32000));
                    m_pDataItems.Add(dataItem);

                    // Raise event, allow user to specify store stream.
                    IMAP_Client_e_FetchGetStoreStream eArgs = new IMAP_Client_e_FetchGetStoreStream(this,dataItem);
                    imap.OnFetchGetStoreStream(eArgs);
                    // User specified own stream, use it.
                    if(eArgs.Stream != null){
                        dataItem.Stream.Dispose();
                        dataItem.SetStream(eArgs.Stream);
                    }

                    // Read data will complete async and will continue data-items parsing, exit this method.
                    if(ReadData(imap,r,callback,dataItem.Stream)){
                        return;
                    }
                    // Continue processing.
                    //else{
                }

                #endregion

                #region RFC822.SIZE

                else if(r.StartsWith("RFC822.SIZE",false)){
                    /* RFC 3501 7.4.2. FETCH Response.
                        RFC822.SIZE
                            A number expressing the [RFC-2822] size of the message.
                    */

                    // Eat RFC822.SIZE word.
                    r.ReadWord();

                    m_pDataItems.Add(new IMAP_t_Fetch_r_i_Rfc822Size(Convert.ToInt32(r.ReadWord())));
                }

                #endregion

                #region RFC822.TEXT

                else if(r.StartsWith("RFC822.TEXT",false)){
                    /* RFC 3501 7.4.2. FETCH Response.
                        RFC822.TEXT
                            Equivalent to BODY[TEXT].
                    */

                    // Eat RFC822.TEXT word.
                    r.ReadWord();
                    r.ReadToFirstChar();

                    IMAP_t_Fetch_r_i_Rfc822Text dataItem = new IMAP_t_Fetch_r_i_Rfc822Text(new MemoryStreamEx(32000));
                    m_pDataItems.Add(dataItem);

                    // Raise event, allow user to specify store stream.
                    IMAP_Client_e_FetchGetStoreStream eArgs = new IMAP_Client_e_FetchGetStoreStream(this,dataItem);
                    imap.OnFetchGetStoreStream(eArgs);
                    // User specified own stream, use it.
                    if(eArgs.Stream != null){
                        dataItem.Stream.Dispose();
                        dataItem.SetStream(eArgs.Stream);
                    }

                    // Read data will complete async and will continue data-items parsing, exit this method.
                    if(ReadData(imap,r,callback,dataItem.Stream)){
                        return;
                    }
                    // Continue processing.
                    //else{
                }

                #endregion

                #region UID

                else if(r.StartsWith("UID",false)){
                    /* RFC 3501 7.4.2. FETCH Response.
                        UID
                            A number expressing the unique identifier of the message.
                    */

                    // Eat UID word.
                    r.ReadWord();

                    m_pDataItems.Add(new IMAP_t_Fetch_r_i_Uid(Convert.ToInt64(r.ReadWord())));
                }

                #endregion

                #region X-GM-MSGID

                else if(r.StartsWith("X-GM-MSGID",false)){
                    /* http://code.google.com/intl/et/apis/gmail/imap X-GM-MSGID.

                    */

                    // Eat X-GM-MSGID word.
                    r.ReadWord();

                    m_pDataItems.Add(new IMAP_t_Fetch_r_i_X_GM_MSGID(Convert.ToUInt64(r.ReadWord())));
                }

                #endregion

                #region X-GM-THRID

                else if(r.StartsWith("X-GM-THRID",false)){
                    /* http://code.google.com/intl/et/apis/gmail/imap X-GM-THRID.

                    */

                    // Eat X-GM-THRID word.
                    r.ReadWord();

                    m_pDataItems.Add(new IMAP_t_Fetch_r_i_X_GM_THRID(Convert.ToUInt64(r.ReadWord())));
                }

                #endregion

                #region ) - fetch closing.

                else if(r.StartsWith(")",false)){
                    break;
                }

                #endregion

                else{
                    throw new ParseException("Not supported FETCH data-item '" + r.ReadToEnd() + "'.");
                }
            }

            callback(this,new EventArgs<Exception>(null));
        }
コード例 #5
0
ファイル: IMAP_r_u_Fetch.cs プロジェクト: nbhopson/QMail
        /// <summary>
        /// Starts parsing FETCH response.
        /// </summary>
        /// <param name="imap">IMAP cleint.</param>
        /// <param name="line">Initial FETCH response line.</param>
        /// <param name="callback">Callback to be called when fetch completed.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>imap</b>,<b>line</b> or <b>callback</b> is null reference.</exception>
        internal void ParseAsync(IMAP_Client imap,string line,EventHandler<EventArgs<Exception>> callback)
        {
            if(imap == null){
                throw new ArgumentNullException("imap");
            }
            if(line == null){
                throw new ArgumentNullException("line");
            }
            if(callback == null){
                throw new ArgumentNullException("callback");
            }

            /* RFC 3501 7.4.2. FETCH Response.
                Example:    S: * 23 FETCH (FLAGS (\Seen) RFC822.SIZE 44827)
            */

            StringReader r = new StringReader(line);

            // Eat '*'
            r.ReadWord();
            // Parse seqNo
            m_MsgSeqNo = Convert.ToInt32(r.ReadWord());
            // Eat 'FETCH'
            r.ReadWord();
            // Eat '(', if list of fetch data-items.
            r.ReadToFirstChar();
            if(r.StartsWith("(")){
                r.ReadSpecifiedLength(1);
            }

            ParseDataItems(imap,r,callback);
        }
コード例 #6
0
        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 (!this.IsAuthenticated)
            {
                TcpStream.WriteLine(string.Format("{0} NO Authenticate first !", cmdTag));
                return;
            }
            if (SelectedMailbox.Length == 0)
            {
                this.TcpStream.WriteLine(string.Format("{0} NO Select mailbox first !", cmdTag));
                return;
            }

            TcpStream.MemoryBuffer = true;

            // Store start time
            long startTime = DateTime.Now.Ticks;

            IMAP_MessageItems_enum messageItems = IMAP_MessageItems_enum.None;

            #region Parse parameters

            string[] args = ParseParams(argsText);
            if (args.Length != 2)
            {
                this.TcpStream.WriteLine(string.Format("{0} BAD Invalid arguments", cmdTag));
                return;
            }

            IMAP_SequenceSet sequenceSet = new IMAP_SequenceSet();
            // Just try if it can be parsed as sequence-set
            try
            {
                if (uidFetch)
                {
                    if (m_pSelectedFolder.Messages.Count > 0)
                    {
                        sequenceSet.Parse(args[0], m_pSelectedFolder.Messages[m_pSelectedFolder.Messages.Count - 1].UID);
                    }
                }
                else
                {
                    sequenceSet.Parse(args[0], m_pSelectedFolder.Messages.Count);
                }
            }
            // This isn't valid sequnce-set value
            catch
            {
                this.TcpStream.WriteLine(string.Format("{0} BAD Invalid <sequnce-set> value '{1}' Syntax: {{<command-tag> FETCH <sequnce-set> (<fetch-keys>)}}!", cmdTag, args[0]));
                return;
            }

            // 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";
            }

            // Start parm parsing from left to end in while loop while params parsed or bad param found
            ArrayList fetchFlags = new ArrayList();
            StringReader argsReader = new StringReader(fetchItems.Trim());
            while (argsReader.Available > 0)
            {
                argsReader.ReadToFirstChar();

                #region BODYSTRUCTURE

                // BODYSTRUCTURE
                if (argsReader.StartsWith("BODYSTRUCTURE"))
                {
                    argsReader.ReadSpecifiedLength("BODYSTRUCTURE".Length);

                    fetchFlags.Add(new object[] { "BODYSTRUCTURE" });
                    messageItems |= IMAP_MessageItems_enum.BodyStructure;
                }

                #endregion

                #region BODY, BODY[<section>]<<partial>>, BODY.PEEK[<section>]<<partial>>

                // BODY, BODY[<section>]<<partial>>, BODY.PEEK[<section>]<<partial>>
                else if (argsReader.StartsWith("BODY"))
                {
                    // Remove BODY
                    argsReader.ReadSpecifiedLength("BODY".Length);

                    bool peek = false;
                    // BODY.PEEK
                    if (argsReader.StartsWith(".PEEK"))
                    {
                        // Remove .PEEK
                        argsReader.ReadSpecifiedLength(".PEEK".Length);

                        peek = true;
                    }

                    // [<section>]<<partial>>
                    if (argsReader.StartsWith("["))
                    {
                        // Read value between []
                        string section = "";
                        try
                        {
                            section = argsReader.ReadParenthesized();
                        }
                        catch
                        {
                            this.TcpStream.WriteLine(cmdTag + " BAD Invalid BODY[], closing ] parenthesize is missing !");
                            return;
                        }

                        string originalSectionValue = section;
                        string mimePartsSpecifier = "";
                        string sectionType = "";
                        string sectionArgs = "";
                        /* Validate <section>
                             Section can be:
                                ""                                                    - entire message								
                                [MimePartsSepcifier.]HEADER                           - message header
                                [MimePartsSepcifier.]HEADER.FIELDS (headerFields)     - message header fields
                                [MimePartsSepcifier.]HEADER.FIELDS.NOT (headerFields) - message header fields except requested
                                [MimePartsSepcifier.]TEXT                             - message text
                                [MimePartsSepcifier.]MIME                             - same as header, different response
                        */
                        if (section.Length > 0)
                        {
                            string[] section_args = section.Split(new char[] { ' ' }, 2);
                            section = section_args[0];
                            if (section_args.Length == 2)
                            {
                                sectionArgs = section_args[1];
                            }

                            if (section.EndsWith("HEADER"))
                            {
                                // Remove HEADER from end
                                section = section.Substring(0, section.Length - "HEADER".Length);

                                sectionType = "HEADER";
                                messageItems |= IMAP_MessageItems_enum.Header;
                            }
                            else if (section.EndsWith("HEADER.FIELDS"))
                            {
                                // Remove HEADER.FIELDS from end
                                section = section.Substring(0, section.Length - "HEADER.FIELDS".Length);

                                sectionType = "HEADER.FIELDS";
                                messageItems |= IMAP_MessageItems_enum.Header;
                            }
                            else if (section.EndsWith("HEADER.FIELDS.NOT"))
                            {
                                // Remove HEADER.FIELDS.NOT from end
                                section = section.Substring(0, section.Length - "HEADER.FIELDS.NOT".Length);

                                sectionType = "HEADER.FIELDS.NOT";
                                messageItems |= IMAP_MessageItems_enum.Header;
                            }
                            else if (section.EndsWith("TEXT"))
                            {
                                // Remove TEXT from end
                                section = section.Substring(0, section.Length - "TEXT".Length);

                                sectionType = "TEXT";
                                messageItems |= IMAP_MessageItems_enum.Message;
                            }
                            else if (section.EndsWith("MIME"))
                            {
                                // Remove MIME from end
                                section = section.Substring(0, section.Length - "MIME".Length);

                                sectionType = "MIME";
                                messageItems = IMAP_MessageItems_enum.Header;
                            }

                            // Remove last ., if there is any
                            if (section.EndsWith("."))
                            {
                                section = section.Substring(0, section.Length - 1);
                            }

                            // MimePartsSepcifier is specified, validate it. It can contain numbers only.
                            if (section.Length > 0)
                            {
                                // Now we certainly need full message, because nested mime parts wanted
                                messageItems |= IMAP_MessageItems_enum.Message;

                                string[] sectionParts = section.Split('.');
                                foreach (string sectionPart in sectionParts)
                                {
                                    if (!Core.IsNumber(sectionPart))
                                    {
                                        this.TcpStream.WriteLine(string.Format("{0} BAD Invalid BODY[<section>] argument. Invalid <section>: {1}", cmdTag, section));
                                        return;
                                    }
                                }

                                mimePartsSpecifier = section;
                            }
                        }
                        else
                        {
                            messageItems |= IMAP_MessageItems_enum.Message;
                        }

                        long startPosition = -1;
                        long length = -1;
                        // See if partial fetch
                        if (argsReader.StartsWith("<"))
                        {
                            /* <partial> syntax:
									startPosition[.endPosition]							  
							*/

                            // Read partial value between <>
                            string partial = "";
                            try
                            {
                                partial = argsReader.ReadParenthesized();
                            }
                            catch
                            {
                                this.TcpStream.WriteLine(string.Format("{0} BAD Invalid BODY[]<start[.length]>, closing > parenthesize is missing !", cmdTag));
                                return;
                            }

                            string[] start_length = partial.Split('.');

                            // Validate <partial>
                            if (start_length.Length == 0 || start_length.Length > 2 || !Core.IsNumber(start_length[0]) || (start_length.Length == 2 && !Core.IsNumber(start_length[1])))
                            {
                                this.TcpStream.WriteLine(string.Format("{0} BAD Invalid BODY[]<partial> argument. Invalid <partial>: {1}", cmdTag, partial));
                                return;
                            }

                            startPosition = Convert.ToInt64(start_length[0]);
                            if (start_length.Length == 2)
                            {
                                length = Convert.ToInt64(start_length[1]);
                            }
                        }

                        // object[] structure for BODY[]
                        //	fetchFlagName
                        //	isPeek
                        //	mimePartsSpecifier
                        //  originalSectionValue
                        //	sectionType
                        //	sectionArgs
                        //	startPosition
                        //	length
                        fetchFlags.Add(new object[] { "BODY[]", peek, mimePartsSpecifier, originalSectionValue, sectionType, sectionArgs, startPosition, length });
                    }
                    // BODY
                    else
                    {
                        fetchFlags.Add(new object[] { "BODY" });
                        messageItems |= IMAP_MessageItems_enum.BodyStructure;
                    }
                }

                #endregion

                #region ENVELOPE

                // ENVELOPE
                else if (argsReader.StartsWith("ENVELOPE"))
                {
                    argsReader.ReadSpecifiedLength("ENVELOPE".Length);

                    fetchFlags.Add(new object[] { "ENVELOPE" });
                    messageItems |= IMAP_MessageItems_enum.Envelope;
                }

                #endregion

                #region FLAGS

                // FLAGS
                //	The flags that are set for this message.
                else if (argsReader.StartsWith("FLAGS"))
                {
                    argsReader.ReadSpecifiedLength("FLAGS".Length);

                    fetchFlags.Add(new object[] { "FLAGS" });
                }

                #endregion

                #region INTERNALDATE

                // INTERNALDATE
                else if (argsReader.StartsWith("INTERNALDATE"))
                {
                    argsReader.ReadSpecifiedLength("INTERNALDATE".Length);

                    fetchFlags.Add(new object[] { "INTERNALDATE" });
                }

                #endregion

                #region RFC822.HEADER

                // RFC822.HEADER
                else if (argsReader.StartsWith("RFC822.HEADER"))
                {
                    argsReader.ReadSpecifiedLength("RFC822.HEADER".Length);

                    fetchFlags.Add(new object[] { "RFC822.HEADER" });
                    messageItems |= IMAP_MessageItems_enum.Header;
                }

                #endregion

                #region RFC822.SIZE

                // RFC822.SIZE
                //	The [RFC-2822] size of the message.
                else if (argsReader.StartsWith("RFC822.SIZE"))
                {
                    argsReader.ReadSpecifiedLength("RFC822.SIZE".Length);

                    fetchFlags.Add(new object[] { "RFC822.SIZE" });
                }

                #endregion

                #region RFC822.TEXT

                // RFC822.TEXT
                else if (argsReader.StartsWith("RFC822.TEXT"))
                {
                    argsReader.ReadSpecifiedLength("RFC822.TEXT".Length);

                    fetchFlags.Add(new object[] { "RFC822.TEXT" });
                    messageItems |= IMAP_MessageItems_enum.Message;
                }

                #endregion

                #region RFC822

                // RFC822 NOTE: RFC822 must be below RFC822.xxx or is parsed wrong !
                else if (argsReader.StartsWith("RFC822"))
                {
                    argsReader.ReadSpecifiedLength("RFC822".Length);

                    fetchFlags.Add(new object[] { "RFC822" });
                    messageItems |= IMAP_MessageItems_enum.Message;
                }

                #endregion

                #region UID

                // UID
                //	The unique identifier for the message.
                else if (argsReader.StartsWith("UID"))
                {
                    argsReader.ReadSpecifiedLength("UID".Length);

                    fetchFlags.Add(new object[] { "UID" });
                }

                #endregion

                // This must be unknown fetch flag
                else
                {
                    this.TcpStream.WriteLine(string.Format("{0} BAD Invalid fetch-items argument. Unkown part starts from: {1}", cmdTag, argsReader.SourceString));
                    return;
                }
            }

            #endregion

            // ToDo: ??? But non of the servers do it ?
            // 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;
            //	}

            // Create buffered writer, so we make less network calls.

            for (int i = 0; i < m_pSelectedFolder.Messages.Count; i++)
            {
                IMAP_Message msg = m_pSelectedFolder.Messages[i];

                // For UID FETCH we must compare UIDs and for normal FETCH message numbers. 
                bool sequenceSetContains = false;
                if (uidFetch)
                {
                    sequenceSetContains = sequenceSet.Contains(msg.UID);
                }
                else
                {
                    sequenceSetContains = sequenceSet.Contains(i + 1);
                }

                if (sequenceSetContains)
                {
                    IMAP_eArgs_MessageItems eArgs = null;
                    // Get message items only if they are needed.
                    if (messageItems != IMAP_MessageItems_enum.None)
                    {
                        // Raise event GetMessageItems to get all neccesary message itmes
                        eArgs = ImapServer.OnGetMessageItems(this, msg, messageItems);

                        // Message doesn't exist any more, notify email client.
                        if (!eArgs.MessageExists)
                        {
                            TcpStream.Write("* " + msg.SequenceNo + " EXPUNGE");
                            ImapServer.OnDeleteMessage(this, msg);
                            m_pSelectedFolder.Messages.Remove(msg);
                            i--;
                            continue;
                        }
                        try
                        {
                            // Ensure that all requested items were provided.
                            eArgs.Validate();
                        }
                        catch (Exception x)
                        {
                            ImapServer.OnSysError(x.Message, x);
                            this.TcpStream.WriteLine(string.Format("{0} NO Internal IMAP server component error: {1}", cmdTag, x.Message));
                            return;
                        }
                    }

                    // Write fetch start data "* msgNo FETCH ("
                    TcpStream.Write("* " + (i + 1) + " FETCH (");

                    IMAP_MessageFlags msgFlagsOr = msg.Flags;
                    // Construct reply here, based on requested fetch items
                    int nCount = 0;
                    foreach (object[] fetchFlag in fetchFlags)
                    {
                        string fetchFlagName = (string)fetchFlag[0];

                        #region BODY

                        // BODY
                        if (fetchFlagName == "BODY")
                        {
                            // Sets \seen flag
                            msg.SetFlags(msg.Flags | IMAP_MessageFlags.Seen);

                            // BODY ()
                            TcpStream.Write("BODY " + eArgs.BodyStructure);
                        }

                        #endregion

                        #region BODY[], BODY.PEEK[]

                        // BODY[<section>]<<partial>>, BODY.PEEK[<section>]<<partial>>
                        else if (fetchFlagName == "BODY[]")
                        {
                            // Force to write all buffered data.
                            TcpStream.Flush();

                            // object[] structure for BODY[]
                            //	fetchFlagName
                            //	isPeek
                            //	mimePartsSpecifier
                            //  originalSectionValue
                            //	sectionType
                            //	sectionArgs
                            //	startPosition
                            //	length
                            bool isPeek = (bool)fetchFlag[1];
                            string mimePartsSpecifier = (string)fetchFlag[2];
                            string originalSectionValue = (string)fetchFlag[3];
                            string sectionType = (string)fetchFlag[4];
                            string sectionArgs = (string)fetchFlag[5];
                            long startPosition = (long)fetchFlag[6];
                            long length = (long)fetchFlag[7];

                            // Difference between BODY[] and BODY.PEEK[] is that .PEEK won't set seen flag
                            if (!isPeek)
                            {
                                // Sets \seen flag
                                msg.SetFlags(msg.Flags | IMAP_MessageFlags.Seen);
                            }

                            /* Section value:
                                ""                - entire message								
                                HEADER            - message header
                                HEADER.FIELDS     - message header fields
                                HEADER.FIELDS.NOT - message header fields except requested
                                TEXT              - message text
                                MIME              - same as header, different response
                            */
                            Stream dataStream = null;
                            if (sectionType == "" && mimePartsSpecifier == "")
                            {
                                dataStream = eArgs.MessageStream;
                            }
                            else
                            {
                                Mime parser = null;
                                try
                                {
                                    if (eArgs.MessageStream == null)
                                    {
                                        parser = Mime.Parse(eArgs.Header);
                                    }
                                    else
                                    {
                                        parser = Mime.Parse(eArgs.MessageStream);
                                    }
                                }
                                // Invalid message, parsing failed
                                catch
                                {
                                    parser = Mime.CreateSimple(new AddressList(), new AddressList(), "BAD Message", "This is BAD message, mail server failed to parse it !", "");
                                }
                                MimeEntity currentEntity = parser.MainEntity;
                                // Specific mime entity requested, get it
                                if (mimePartsSpecifier != "")
                                {
                                    currentEntity = FetchHelper.GetMimeEntity(parser, mimePartsSpecifier);
                                }

                                if (currentEntity != null)
                                {
                                    if (sectionType == "HEADER")
                                    {
                                        dataStream = new MemoryStream(FetchHelper.GetMimeEntityHeader(currentEntity));
                                    }
                                    else if (sectionType == "HEADER.FIELDS")
                                    {
                                        dataStream = new MemoryStream(FetchHelper.ParseHeaderFields(sectionArgs, currentEntity));
                                    }
                                    else if (sectionType == "HEADER.FIELDS.NOT")
                                    {
                                        dataStream = new MemoryStream(FetchHelper.ParseHeaderFieldsNot(sectionArgs, currentEntity));
                                    }
                                    else if (sectionType == "TEXT")
                                    {
                                        try
                                        {
                                            if (currentEntity.DataEncoded != null)
                                            {
                                                dataStream = new MemoryStream(currentEntity.DataEncoded);
                                            }
                                        }
                                        catch
                                        { // This probably multipart entity, data isn't available
                                        }
                                    }
                                    else if (sectionType == "MIME")
                                    {
                                        dataStream = new MemoryStream(FetchHelper.GetMimeEntityHeader(currentEntity));
                                    }
                                    else if (sectionType == "")
                                    {
                                        try
                                        {
                                            dataStream = new MemoryStream(currentEntity.DataEncoded);
                                        }
                                        catch
                                        { // This probably multipart entity, data isn't available
                                        }
                                    }
                                }
                            }

                            // Partial fetch. Reports <origin position> in fetch reply.
                            if (startPosition > -1)
                            {
                                if (dataStream == null)
                                {
                                    this.TcpStream.Write("BODY[" + originalSectionValue + "]<" + startPosition.ToString() + "> \"\"\r\n");
                                }
                                else
                                {
                                    long lengthToSend = length;
                                    if (lengthToSend == -1)
                                    {
                                        lengthToSend = (dataStream.Length - dataStream.Position) - startPosition;
                                    }
                                    if ((lengthToSend + startPosition) > (dataStream.Length - dataStream.Position))
                                    {
                                        lengthToSend = (dataStream.Length - dataStream.Position) - startPosition;
                                    }

                                    if (startPosition >= (dataStream.Length - dataStream.Position))
                                    {
                                        this.TcpStream.Write("BODY[" + originalSectionValue + "]<" + startPosition.ToString() + "> \"\"\r\n");
                                    }
                                    else
                                    {
                                        this.TcpStream.Write("BODY[" + originalSectionValue + "]<" + startPosition.ToString() + "> {" + lengthToSend + "}\r\n");
                                        dataStream.Position += startPosition;
                                        this.TcpStream.WriteStream(dataStream, lengthToSend);
                                    }
                                }
                            }
                            // Normal fetch
                            else
                            {
                                if (dataStream == null)
                                {
                                    this.TcpStream.Write("BODY[" + originalSectionValue + "] \"\"\r\n");
                                }
                                else
                                {
                                    this.TcpStream.Write("BODY[" + originalSectionValue + "] {" + (dataStream.Length - dataStream.Position) + "}\r\n");
                                    this.TcpStream.WriteStream(dataStream);
                                }
                            }
                        }

                        #endregion

                        #region BODYSTRUCTURE

                        // BODYSTRUCTURE
                        else if (fetchFlagName == "BODYSTRUCTURE")
                        {
                            TcpStream.Write("BODYSTRUCTURE " + eArgs.BodyStructure);
                        }

                        #endregion

                        #region ENVELOPE

                        // ENVELOPE
                        else if (fetchFlagName == "ENVELOPE")
                        {
                            TcpStream.Write("ENVELOPE " + eArgs.Envelope);
                        }

                        #endregion

                        #region FLAGS

                        // FLAGS
                        else if (fetchFlagName == "FLAGS")
                        {
                            TcpStream.Write("FLAGS (" + msg.FlagsString + ")");
                        }

                        #endregion

                        #region INTERNALDATE

                        // INTERNALDATE
                        else if (fetchFlagName == "INTERNALDATE")
                        {
                            // INTERNALDATE "date"
                            TcpStream.Write("INTERNALDATE \"" + IMAP_Utils.DateTimeToString(msg.InternalDate) + "\"");
                        }

                        #endregion

                        #region RFC822

                        // RFC822
                        else if (fetchFlagName == "RFC822")
                        {
                            // Force to write all buffered data.
                            TcpStream.Flush();

                            // Sets \seen flag
                            msg.SetFlags(msg.Flags | IMAP_MessageFlags.Seen);

                            // RFC822 {size}
                            // msg data
                            this.TcpStream.Write("RFC822 {" + eArgs.MessageSize.ToString() + "}\r\n");
                            this.TcpStream.WriteStream(eArgs.MessageStream);
                        }

                        #endregion

                        #region RFC822.HEADER

                        // RFC822.HEADER
                        else if (fetchFlagName == "RFC822.HEADER")
                        {
                            // Force to write all buffered data.
                            TcpStream.Flush();

                            // RFC822.HEADER {size}
                            // msg header data
                            this.TcpStream.Write("RFC822.HEADER {" + eArgs.Header.Length + "}\r\n");
                            this.TcpStream.Write(eArgs.Header);
                        }

                        #endregion

                        #region RFC822.SIZE

                        // RFC822.SIZE
                        else if (fetchFlagName == "RFC822.SIZE")
                        {
                            // RFC822.SIZE size
                            TcpStream.Write("RFC822.SIZE " + msg.Size);
                        }

                        #endregion

                        #region RFC822.TEXT

                        // RFC822.TEXT
                        else if (fetchFlagName == "RFC822.TEXT")
                        {
                            // Force to write all buffered data.
                            TcpStream.Flush();

                            // Sets \seen flag
                            msg.SetFlags(msg.Flags | IMAP_MessageFlags.Seen);

                            //--- Find body text entity ------------------------------------//
                            Mime parser = Mime.Parse(eArgs.MessageStream);
                            MimeEntity bodyTextEntity = null;
                            if (parser.MainEntity.ContentType == MediaType_enum.NotSpecified)
                            {
                                if (parser.MainEntity.DataEncoded != null)
                                {
                                    bodyTextEntity = parser.MainEntity;
                                }
                            }
                            else
                            {
                                MimeEntity[] entities = parser.MimeEntities;
                                foreach (MimeEntity entity in entities)
                                {
                                    if (entity.ContentType == MediaType_enum.Text_plain)
                                    {
                                        bodyTextEntity = entity;
                                        break;
                                    }
                                }
                            }
                            //----------------------------------------------------------------//

                            // RFC822.TEXT {size}
                            // msg text	
                            byte[] data = null;
                            if (bodyTextEntity != null)
                            {
                                data = bodyTextEntity.DataEncoded;
                            }
                            else
                            {
                                data = System.Text.Encoding.ASCII.GetBytes("");
                            }

                            this.TcpStream.Write("RFC822.TEXT {" + data.Length + "}\r\n");
                            this.TcpStream.Write(data);
                        }

                        #endregion

                        #region UID

                        // UID
                        else if (fetchFlagName == "UID")
                        {
                            TcpStream.Write("UID " + msg.UID);
                        }

                        #endregion

                        nCount++;

                        // Write fetch item separator data " "
                        // We don't write it for last item
                        if (nCount < fetchFlags.Count)
                        {
                            TcpStream.Write(" ");
                        }
                    }

                    // Write fetch end data ")"
                    TcpStream.Write(")\r\n");

                    // Free event args, close message stream, ... .
                    if (eArgs != null)
                    {
                        eArgs.Dispose();
                    }

                    // 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);

                        ImapServer.OnStoreMessageFlags(this, msg);
                    }
                }
            }

            // Force to write all buffered data.
            TcpStream.Flush();

            this.TcpStream.WriteLine(string.Format("{0} OK FETCH completed in {1} seconds", cmdTag, ((DateTime.Now.Ticks - startTime) / (decimal)10000000).ToString("f2")));
        }
コード例 #7
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>
        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();
        }
コード例 #8
0
//
        #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")));
        }
コード例 #9
0
ファイル: MimeUtils.cs プロジェクト: CivilPol/LumiSoft.Net
        /// <summary>
        /// Decodes "encoded-word"'s from the specified text. For more information see RFC 2047.
        /// </summary>
        /// <param name="text">Text to decode.</param>
        /// <returns>Returns decoded text.</returns>
        public static string DecodeWords(string text)
        {
            if(text == null){
                return null;
            }

            /* RFC 2047 2. Syntax of encoded-words.
                An 'encoded-word' is defined by the following ABNF grammar.  The
                notation of RFC 822 is used, with the exception that white space
                characters MUST NOT appear between components of an 'encoded-word'.

                encoded-word = "=?" charset "?" encoding "?" encoded-text "?="
                charset      = token    ; see section 3
                encoding     = token    ; see section 4
                token        = 1*<Any CHAR except SPACE, CTLs, and especials>
                especials    = "(" / ")" / "<" / ">" / "@" / "," / ";" / ":" / "
                               <"> / "/" / "[" / "]" / "?" / "." / "="
                encoded-text = 1*<Any printable ASCII character other than "?" or SPACE>
                                       ; (but see "Use of encoded-words in message headers", section 5)

                Both 'encoding' and 'charset' names are case-independent.  Thus the
                charset name "ISO-8859-1" is equivalent to "iso-8859-1", and the
                encoding named "Q" may be spelled either "Q" or "q".

                An 'encoded-word' may not be more than 75 characters long, including
                'charset', 'encoding', 'encoded-text', and delimiters.  If it is
                desirable to encode more text than will fit in an 'encoded-word' of
                75 characters, multiple 'encoded-word's (separated by CRLF SPACE) may
                be used.
              
                IMPORTANT: 'encoded-word's are designed to be recognized as 'atom's
                by an RFC 822 parser.  As a consequence, unencoded white space
                characters (such as SPACE and HTAB) are FORBIDDEN within an
                'encoded-word'.  For example, the character sequence

                =?iso-8859-1?q?this is some text?=

                would be parsed as four 'atom's, rather than as a single 'atom' (by
                an RFC 822 parser) or 'encoded-word' (by a parser which understands
                'encoded-words').  The correct way to encode the string "this is some
                text" is to encode the SPACE characters as well, e.g.

                =?iso-8859-1?q?this=20is=20some=20text?=
            */

            StringReader  r      = new StringReader(text);
            StringBuilder retVal = new StringBuilder();

            // We need to loop all words, if encoded word, decode it, othwerwise just append to return value.
            bool lastIsEncodedWord = false;
            while(r.Available > 0){
                string whiteSpaces = r.ReadToFirstChar();

                // Probably is encoded-word, we try to parse it.
                if(r.StartsWith("=?") && r.SourceString.IndexOf("?=") > -1){
                    StringBuilder encodedWord = new StringBuilder();
                    string        decodedWord = null;

                    try{
                        // NOTE: We can't read encoded word and then split !!!, we need to read each part.
                    
                        // Remove =?
                        encodedWord.Append(r.ReadSpecifiedLength(2));

                        // Read charset
                        string charset = r.QuotedReadToDelimiter('?');
                        encodedWord.Append(charset + "?");

                        // Read encoding
                        string encoding = r.QuotedReadToDelimiter('?');
                        encodedWord.Append(encoding + "?");

                        // Read text
                        string encodedText = r.QuotedReadToDelimiter('?');
                        encodedWord.Append(encodedText + "?");

                        // We must have remaining '=' here
                        if(r.StartsWith("=")){
                            encodedWord.Append(r.ReadSpecifiedLength(1));

                            Encoding c = Encoding.GetEncoding(charset);
                            if(encoding.ToLower() == "q"){
                                decodedWord = Core.QDecode(c,encodedText);
                            }
                            else if(encoding.ToLower() == "b"){
                                decodedWord = c.GetString(Core.Base64Decode(Encoding.Default.GetBytes(encodedText)));
                            }
                        }
                    }
                    catch{
                        // Not encoded-word or contains unknwon charset/encoding, so leave
                        // encoded-word as is.
                    }

                    /* RFC 2047 6.2.
                        When displaying a particular header field that contains multiple
                        'encoded-word's, any 'linear-white-space' that separates a pair of
                        adjacent 'encoded-word's is ignored.  (This is to allow the use of
                        multiple 'encoded-word's to represent long strings of unencoded text,
                        without having to separate 'encoded-word's where spaces occur in the
                        unencoded text.)
                    */
                    if(!lastIsEncodedWord){
                        retVal.Append(whiteSpaces);
                    }

                    // Decoding failed for that encoded-word, leave encoded-word as is.
                    if(decodedWord == null){
                        retVal.Append(encodedWord.ToString());
                    }
                    // We deocded encoded-word successfully.
                    else{
                        retVal.Append(decodedWord);
                    }

                    lastIsEncodedWord = true;
                }
                // Normal word.
                else if(r.StartsWithWord()){
                    retVal.Append(whiteSpaces + r.ReadWord(false));
                    lastIsEncodedWord = false;
                }
                // We have some separator or parenthesize.
                else{
                   retVal.Append(whiteSpaces + r.ReadSpecifiedLength(1));
                }
            }

            return retVal.ToString();
        }
コード例 #10
0
ファイル: IMAP_Client.cs プロジェクト: Klaudit/inbox2_desktop
        /// <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();
        }
コード例 #11
0
ファイル: IMAP_Client.cs プロジェクト: Klaudit/inbox2_desktop
        /// <summary>
        /// Gets specified message from server and stores to specified stream.
        /// </summary>
        /// <param name="uid">Message UID which to get.</param>
        /// <param name="storeStream">Stream where to store message.</param>
        /// <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 void FetchMessage(int uid, Stream storeStream)
        {
            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.");
            }

            // Send fetch command line to server.
            // mwa fix for gmail change, message was being marked as read automatically without the PEEK modifier
            string line = GetNextCmdTag() + " UID FETCH " + uid + " BODY.PEEK[]";
            int countWritten = this.TcpStream.WriteLine(line);
            LogAddWrite(countWritten, line);

            // Read un-tagged response lines while we get final response line.
            while (true)
            {
                line = this.ReadLine();

                // We have un-tagged resposne.
                if (line.StartsWith("*"))
                {
                    if (IsStatusResponse(line))
                    {
                        ProcessStatusResponse(line);
                    }
                    else if (line.ToUpper().ToString().IndexOf("BODY[") > -1)
                    {
                        if (line.IndexOf('{') > -1)
                        {
                            StringReader r = new StringReader(line);
                            while (r.Available > 0 && !r.StartsWith("{"))
                            {
                                r.ReadSpecifiedLength(1);
                            }
                            int sizeOfData = Convert.ToInt32(r.ReadParenthesized());
                            this.TcpStream.ReadFixedCount(storeStream, sizeOfData);
                            LogAddRead(sizeOfData, "Readed " + sizeOfData + " bytes.");
                            line = this.ReadLine();
                        }
                    }
                }
                else
                {
                    break;
                }
            }

            if (!RemoveCmdTag(line).ToUpper().StartsWith("OK"))
            {
                throw new IMAP_ClientException(line);
            }
        }
コード例 #12
0
        public byte[] GetFilters()
        {
            if (IsDisposed)
            {
                throw new ObjectDisposedException(GetType().Name);
            }
            if (!IsConnected)
            {
                throw new InvalidOperationException("You must connect first.");
            }
            if (!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.");
            }

            // Send fetch command line to server.
            string line = GetNextCmdTag() + " X-GET-FILTER ";
            int countWritten = TcpStream.WriteLine(line);
            LogAddWrite(countWritten, line);
            using (MemoryStream store = new MemoryStream())
            {
                // Read un-tagged response lines while we get final response line.            
                while (true)
                {
                    line = ReadLine();

                    // We have un-tagged resposne.
                    if (line.StartsWith("*"))
                    {
                        if (IsStatusResponse(line))
                        {
                            ProcessStatusResponse(line);
                        }
                        else if (line.ToUpper().IndexOf("FILTER") > -1)
                        {
                            if (line.IndexOf('{') > -1)
                            {
                                StringReader r = new StringReader(line);
                                while (r.Available > 0 && !r.StartsWith("{"))
                                {
                                    r.ReadSpecifiedLength(1);
                                }
                                int sizeOfData = Convert.ToInt32(r.ReadParenthesized());
                                TcpStream.ReadFixedCount(store, sizeOfData);
                                LogAddRead(sizeOfData, "Readed " + sizeOfData + " bytes.");
                                line = ReadLine();
                            }
                        }
                    }
                    else
                    {
                        break;
                    }
                }

                if (!RemoveCmdTag(line).ToUpper().StartsWith("OK"))
                {
                    throw new IMAP_ClientException(line);
                }
                byte[] buffer =  store.GetBuffer();
                if (buffer.Length>0)
                {
                    return Convert.FromBase64String(Encoding.UTF8.GetString(buffer));
                }
                else
                {
                    return null;
                }
            }

        }
コード例 #13
0
ファイル: FTP_Client.cs プロジェクト: andreikalatsei/milskype
        /// <summary>
        /// Gets files and directories in the current server directory.
        /// </summary>
        /// <param name="path">Directory or file name which listing to get. Value null means current directory will be listed.</param>
        /// <returns>Returns current working directory listing.</returns>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="InvalidOperationException">Is raised when FTP client is not connected or FTP data connection has active read/write operation.</exception>
        /// <exception cref="FTP_ClientException">Is raised when FTP server returns error.</exception>
        public FTP_ListItem[] GetList(string path)
        {
            if(this.IsDisposed){
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if(!this.IsConnected){
                throw new InvalidOperationException("You must connect first.");
            }
            if(m_pDataConnection.IsActive){
                throw new InvalidOperationException("There is already active read/write operation on data connection.");
            }

            List<FTP_ListItem> retVal = new List<FTP_ListItem>();

            // Set transfer mode
            SetTransferType(TransferType.Binary);

            if(m_TransferMode == FTP_TransferMode.Passive){
                Pasv();
            }
            else{
                Port();
            }

            // If FTP server supports MLSD command, use it to get directory listing.
            // MLSD is standard way to get dir listing, while LIST command isn't any strict standard.
            bool mlsdSupported = false;
            foreach(string feature in m_pExtCapabilities){
                if(feature.ToLower().StartsWith("mlsd")){
                    mlsdSupported = true;
                    break;
                }
            }

            #region MLSD

            if(mlsdSupported){
                if(string.IsNullOrEmpty(path)){
                    WriteLine("MLSD");
                }
                else{
                    WriteLine("MLSD " + path);
                }

                string[] response = ReadResponse();
                if(!response[0].StartsWith("1")){
                    throw new FTP_ClientException(response[0]);
                }

                MemoryStream ms = new MemoryStream();
                m_pDataConnection.ReadAll(ms);

                response = ReadResponse();
                if(!response[0].StartsWith("2")){
                    throw new FTP_ClientException(response[0]);
                }

                byte[] lineBuffer = new byte[8000];
                ms.Position = 0;
                SmartStream mlsdStream = new SmartStream(ms,true);
                while(true){
                    SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(lineBuffer,SizeExceededAction.JunkAndThrowException);
                    mlsdStream.ReadLine(args,false);
                    if(args.Error != null){
                        throw args.Error;
                    }
                    string line = args.LineUtf8;

                    // We reached end of stream, we readed whole list sucessfully.
                    if(line == null){
                        break;
                    }
                    else{
                        string[] parameters = line.Substring(0,line.LastIndexOf(';')).Split(';');
                        string   name       = line.Substring(line.LastIndexOf(';') + 1).Trim();

                        string   type     = "";
                        long     size     = 0;
                        DateTime modified = DateTime.MinValue;
                        foreach(string parameter in parameters){
                            string[] name_value = parameter.Split('=');
                            if(name_value[0].ToLower() == "type"){
                                type = name_value[1].ToLower();
                            }
                            else if(name_value[0].ToLower() == "size"){
                                size = Convert.ToInt32(name_value[1]);
                            }
                            else if(name_value[0].ToLower() == "modify"){
                                modified = DateTime.ParseExact(name_value[1],"yyyyMMddHHmmss",System.Globalization.DateTimeFormatInfo.InvariantInfo);
                            }
                            else{
                                // Other options won't interest us, skip them.
                            }
                        }

                        if(type == "dir"){
                            retVal.Add(new FTP_ListItem(name,0,modified,true));
                        }
                        else if(type == "file"){
                            retVal.Add(new FTP_ListItem(name,size,modified,false));
                        }
                    }
                }
            }

            #endregion

            #region LIST

            else{
                if(string.IsNullOrEmpty(path)){
                    WriteLine("LIST");
                }
                else{
                    WriteLine("LIST " + path);
                }

                string[] response = ReadResponse();
                if(!response[0].StartsWith("1")){
                    throw new FTP_ClientException(response[0]);
                }

                MemoryStream ms = new MemoryStream();
                m_pDataConnection.ReadAll(ms);

                response = ReadResponse();
                if(!response[0].StartsWith("2")){
                    throw new FTP_ClientException(response[0]);
                }

                ms.Position = 0;
                SmartStream listStream =  new SmartStream(ms,true);

                string[] winDateFormats = new string[]{"M-d-yy h:mmtt"};
                string[] unixFormats    = new string[]{"MMM d H:mm","MMM d yyyy"};

                SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[8000],SizeExceededAction.JunkAndThrowException);
                while(true){
                    listStream.ReadLine(args,false);
                    if(args.Error != null){
                        throw args.Error;
                    }
                    else if(args.BytesInBuffer == 0){
                        break;
                    }
                    string line = args.LineUtf8;

                    // Dedect listing.
                    string listingType = "unix";
                    if(line != null){
                        StringReader r = new StringReader(line);
                        DateTime modified;
                        if(DateTime.TryParseExact(r.ReadWord() + " " + r.ReadWord(),new string[]{"MM-dd-yy hh:mmtt"},System.Globalization.DateTimeFormatInfo.InvariantInfo,System.Globalization.DateTimeStyles.None,out modified)){
                            listingType = "win";
                        }
                    }

                    try{
                        // Windows listing.
                        if(listingType == "win"){
                            // MM-dd-yy hh:mm <DIR> directoryName
                            // MM-dd-yy hh:mm size  fileName

                            StringReader r = new StringReader(line);
                            // Read date
                            DateTime modified = DateTime.ParseExact(r.ReadWord() + " " + r.ReadWord(),winDateFormats,System.Globalization.DateTimeFormatInfo.InvariantInfo,System.Globalization.DateTimeStyles.None);

                            r.ReadToFirstChar();
                            // We have directory.
                            if(r.StartsWith("<dir>",false)){
                                r.ReadSpecifiedLength(5);
                                r.ReadToFirstChar();

                                retVal.Add(new FTP_ListItem(r.ReadToEnd(),0,modified,true));
                            }
                            // We have file
                            else{
                                // Read file size
                                long size = Convert.ToInt64(r.ReadWord());
                                r.ReadToFirstChar();

                                retVal.Add(new FTP_ListItem(r.ReadToEnd(),size,modified,false));
                            }
                        }
                        // Unix listing
                        else{
                            // "d"directoryAtttributes xx xx xx 0 MMM d HH:mm/yyyy directoryName
                            // fileAtttributes xx xx xx fileSize MMM d HH:mm/yyyy fileName

                            StringReader r = new StringReader(line);
                            string attributes = r.ReadWord();
                            r.ReadWord();
                            r.ReadWord();
                            r.ReadWord();
                            long size = Convert.ToInt64(r.ReadWord());
                            DateTime modified = DateTime.ParseExact(r.ReadWord() + " " + r.ReadWord() + " " + r.ReadWord(),unixFormats,System.Globalization.DateTimeFormatInfo.InvariantInfo,System.Globalization.DateTimeStyles.None);
                            r.ReadToFirstChar();
                            string name = r.ReadToEnd();
                            if(name != "." && name != ".."){
                                if(attributes.StartsWith("d")){
                                    retVal.Add(new FTP_ListItem(name,0,modified,true));
                                }
                                else{
                                    retVal.Add(new FTP_ListItem(name,size,modified,false));
                                }
                            }
                        }
                    }
                    catch{
                        // Skip unknown entries.
                    }
                }
            }

            #endregion

            return retVal.ToArray();
        }
コード例 #14
0
ファイル: IMAP_Utils.cs プロジェクト: ChuckLafferty/bugnet
        /// <summary>
        /// Reads IMAP string/astring/nstring/utf8-quoted from string reader.
        /// </summary>
        /// <param name="reader">String reader.</param>
        /// <returns>Returns IMAP string.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>reader</b> is null reference.</exception>
        internal static string ReadString(StringReader reader)
        {
            if(reader == null){
                throw new ArgumentNullException("reader");
            }

            reader.ReadToFirstChar();

            // utf8-quoted
            if(reader.StartsWith("*\"")){
                reader.ReadSpecifiedLength(1);

                return reader.ReadWord();
            }
            // string/astring/nstring
            else{
                string word = reader.ReadWord();

                // nstring
                if(string.Equals(word,"NIL",StringComparison.InvariantCultureIgnoreCase)){
                    return null;
                }

                return word;
            }
        }
コード例 #15
0
ファイル: IMAP_Utils.cs プロジェクト: DJGosnell/LumiSoft.Net
        /// <summary>
        /// Reads IMAP string-literal/string/astring/nstring/utf8-quoted from string reader.
        /// </summary>
        /// <param name="reader">String reader.</param>
        /// <returns>Returns IMAP string.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>reader</b> is null reference.</exception>
        public static string ReadString(StringReader reader)
        {
            if(reader == null){
                throw new ArgumentNullException("reader");
            }

            reader.ReadToFirstChar();

            // We have string-literal.
            if(reader.SourceString.StartsWith("{")){
                int literalSize = Convert.ToInt32(reader.ReadParenthesized());
                // Literal has CRLF ending, skip it.
                reader.ReadSpecifiedLength(2);
                                                
                return reader.ReadSpecifiedLength(literalSize);
            }
            // utf8-quoted old rfc 5738
            else if(reader.StartsWith("*\"")){
                reader.ReadSpecifiedLength(1);

                return reader.ReadWord();
            }
            // string/astring/nstring
            else{
                string word = reader.ReadWord();
                
                // nstring
                if(string.Equals(word,"NIL",StringComparison.InvariantCultureIgnoreCase)){
                    return null;
                }

                return word;
            }
        }
コード例 #16
0
        private void APPEND(string cmdTag,string cmdText)
        {
            /* RFC 3501 6.3.11. APPEND Command.
                Arguments:  mailbox name
                            OPTIONAL flag parenthesized list
                            OPTIONAL date/time string
                            message literal

                Responses:  no specific responses for this command

                Result:     OK - append completed
                            NO - append error: can't append to that mailbox, error
                                 in flags or date/time or message text
                            BAD - command unknown or arguments invalid

                The APPEND command appends the literal argument as a new message
                to the end of the specified destination mailbox.  This argument
                SHOULD be in the format of an [RFC-2822] message.  8-bit
                characters are permitted in the message.  A server implementation
                that is unable to preserve 8-bit data properly MUST be able to
                reversibly convert 8-bit APPEND data to 7-bit using a [MIME-IMB]
                content transfer encoding.

                    Note: There MAY be exceptions, e.g., draft messages, in
                    which required [RFC-2822] header lines are omitted in
                    the message literal argument to APPEND.  The full
                    implications of doing so MUST be understood and
                    carefully weighed.

                If a flag parenthesized list is specified, the flags SHOULD be set
                in the resulting message; otherwise, the flag list of the
                resulting message is set to empty by default.  In either case, the
                Recent flag is also set.

                If a date-time is specified, the internal date SHOULD be set in
                the resulting message; otherwise, the internal date of the
                resulting message is set to the current date and time by default.

                If the append is unsuccessful for any reason, the mailbox MUST be
                restored to its state before the APPEND attempt; no partial
                appending is permitted.

                If the destination mailbox does not exist, a server MUST return an
                error, and MUST NOT automatically create the mailbox.  Unless it
                is certain that the destination mailbox can not be created, the
                server MUST send the response code "[TRYCREATE]" as the prefix of
                the text of the tagged NO response.  This gives a hint to the
                client that it can attempt a CREATE command and retry the APPEND
                if the CREATE is successful.

                If the mailbox is currently selected, the normal new message
                actions SHOULD occur.  Specifically, the server SHOULD notify the
                client immediately via an untagged EXISTS response.  If the server
                does not do so, the client MAY issue a NOOP command (or failing
                that, a CHECK command) after one or more APPEND commands.

                Example:    C: A003 APPEND saved-messages (\Seen) {310}
                            S: + Ready for literal data
                            C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
                            C: From: Fred Foobar <*****@*****.**>
                            C: Subject: afternoon meeting
                            C: To: [email protected]
                            C: Message-Id: <*****@*****.**>
                            C: MIME-Version: 1.0
                            C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
                            C:
                            C: Hello Joe, do you think we can meet at 3:30 tomorrow?
                            C:
                            S: A003 OK APPEND completed

                    Note: The APPEND command is not used for message delivery,
                    because it does not provide a mechanism to transfer [SMTP]
                    envelope information.
            */

            if(!this.IsAuthenticated){
                m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus(cmdTag,"NO","Authentication required."));

                return;
            }

            // Store start time
			long startTime = DateTime.Now.Ticks;

            #region Parse arguments

            StringReader r = new StringReader(cmdText);
            r.ReadToFirstChar();
            string folder = null;
            if(r.StartsWith("\"")){
                folder = IMAP_Utils.DecodeMailbox(r.ReadWord());
            }
            else{
                folder = IMAP_Utils.DecodeMailbox(r.QuotedReadToDelimiter(' '));
            }
            r.ReadToFirstChar();
            List<string> flags = new List<string>();
            if(r.StartsWith("(")){                
                foreach(string f in r.ReadParenthesized().Split(' ')){
                    if(f.Length > 0 && !flags.Contains(f.Substring(1))){
                        flags.Add(f.Substring(1));
                    }
                }
            }
            r.ReadToFirstChar();
            DateTime date = DateTime.MinValue;
            if(!r.StartsWith("{")){
                date = IMAP_Utils.ParseDate(r.ReadWord());
            }
            int size = Convert.ToInt32(r.ReadParenthesized());
            if(r.Available > 0){
                m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus(cmdTag,"BAD","Error in arguments."));

                return;
            }

            #endregion

            IMAP_e_Append e = OnAppend(folder,flags.ToArray(),date,size,new IMAP_r_ServerStatus(cmdTag,"OK","APPEND command completed in %exectime seconds."));
            
            if(e.Response.IsError){
                m_pResponseSender.SendResponseAsync(e.Response);
            }
            else if(e.Stream == null){
                m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus(cmdTag,"NO","Internal server error: No storage stream available."));
            }
            else{
                m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus("+","Ready for literal data."));
                
                // Create callback which is called when BeginReadFixedCount completes.
                AsyncCallback readLiteralCompletedCallback = delegate(IAsyncResult ar){
                    try{
                        this.TcpStream.EndReadFixedCount(ar);
                        // Log.
                        LogAddRead(size,"Readed " + size + " bytes.");

                        // TODO: Async
                        // Read command line terminating CRLF.
                        SmartStream.ReadLineAsyncOP readLineOP = new SmartStream.ReadLineAsyncOP(new byte[32000],SizeExceededAction.JunkAndThrowException);
                        this.TcpStream.ReadLine(readLineOP,false);
                        // Read command line terminating CRLF failed.
                        if(readLineOP.Error != null){
                            OnError(readLineOP.Error);
                        }
                        // Read command line terminating CRLF succeeded.
                        else{
                            LogAddRead(readLineOP.BytesInBuffer,readLineOP.LineUtf8);

                            // Raise Completed event.
                            e.OnCompleted();

                            m_pResponseSender.SendResponseAsync(IMAP_r_ServerStatus.Parse(e.Response.ToString().TrimEnd().Replace("%exectime",((DateTime.Now.Ticks - startTime) / (decimal)10000000).ToString("f2"))));
                            BeginReadCmd();
                        }
                    }
                    catch(Exception x){
                        OnError(x);
                    }
                };

                this.TcpStream.BeginReadFixedCount(e.Stream,size,readLiteralCompletedCallback,null);
            }
        }
コード例 #17
0
        /// <summary>
        /// Parses rfc 2822 datetime.
        /// </summary>
        /// <param name="date">Date string.</param>
        /// <returns></returns>
        public static DateTime ParseDate(string date)
        {
            /* Rfc 2822 3.3. Date and Time Specification.
                date-time       = [ day-of-week "," ] date FWS time [CFWS]
                date            = day month year
                time            = hour ":" minute [ ":" second ] FWS zone
            */

            /* IMAP date format.
                date-time       = date FWS time [CFWS]
                date            = day-month-year
                time            = hour ":" minute [ ":" second ] FWS zone
            */

            // zone = (( "+" / "-" ) 4DIGIT)

            //--- Replace timezone constants -------//
            /*
             UT  -0000
            GMT  -0000
            EDT  -0400
            EST  -0500
            CDT  -0500
            CST  -0600
            MDT  -0600
            MST  -0700
            PDT  -0700
            PST  -0800
            BST  +0100 British Summer Time
            */

            date = date.ToLower();
            date = date.Replace("ut","-0000");
            date = date.Replace("gmt","-0000");
            date = date.Replace("edt","-0400");
            date = date.Replace("est","-0500");
            date = date.Replace("cdt","-0500");
            date = date.Replace("cst","-0600");
            date = date.Replace("mdt","-0600");
            date = date.Replace("mst","-0700");
            date = date.Replace("pdt","-0700");
            date = date.Replace("pst","-0800");
            date = date.Replace("bst","+0100");
            //----------------------------------------//

            //--- Replace month constants ---//
            date = date.Replace("jan","01");
            date = date.Replace("feb","02");
            date = date.Replace("mar","03");
            date = date.Replace("apr","04");
            date = date.Replace("may","05");
            date = date.Replace("jun","06");
            date = date.Replace("jul","07");
            date = date.Replace("aug","08");
            date = date.Replace("sep","09");
            date = date.Replace("oct","10");
            date = date.Replace("nov","11");
            date = date.Replace("dec","12");
            //-------------------------------//

            //  If date contains optional "day-of-week,", remove it
            if(date.IndexOf(',') > -1){
                date = date.Substring(date.IndexOf(',') + 1);
            }

            // Remove () from date. "Mon, 13 Oct 2003 20:50:57 +0300 (EEST)"
            if(date.IndexOf(" (") > -1){
                date = date.Substring(0,date.IndexOf(" ("));
            }

            int year        = 1900;
            int month       = 1;
            int day         = 1;
            int hour        = -1;
            int minute      = -1;
            int second      = -1;
            int zoneMinutes = -1;

            StringReader s = new StringReader(date);

            //--- Pase date --------------------------------------------------------------------//
            try{
                day = Convert.ToInt32(s.ReadWord(true,new char[]{'.','-',' '},true));
            }
            catch{
                throw new Exception("Invalid date value '" + date + "', invalid day value !");
            }

            try{
                month = Convert.ToInt32(s.ReadWord(true,new char[]{'.','-',' '},true));
            }
            catch{
                throw new Exception("Invalid date value '" + date + "', invalid month value !");
            }

            try{
                year = Convert.ToInt32(s.ReadWord(true,new char[]{'.','-',' '},true));
            }
            catch{
                throw new Exception("Invalid date value '" + date + "', invalid year value !");
            }
            //----------------------------------------------------------------------------------//

            //--- Parse time -------------------------------------------------------------------//
            // Time is optional, so parse it if its included.
            if(s.Available > 0){
                try{
                    hour = Convert.ToInt32(s.ReadWord(true,new char[]{':'},true));
                }
                catch{
                    throw new Exception("Invalid date value '" + date + "', invalid hour value !");
                }

                try{
                    minute = Convert.ToInt32(s.ReadWord(true,new char[]{':'},false));
                }
                catch{
                    throw new Exception("Invalid date value '" + date + "', invalid minute value !");
                }

                s.ReadToFirstChar();
                if(s.StartsWith(":")){
                    s.ReadSpecifiedLength(1);
                    try{
                        string secondString = s.ReadWord(true,new char[]{' '},true);
                        // Milli seconds specified, remove them.
                        if(secondString.IndexOf('.') > -1){
                            secondString = secondString.Substring(0,secondString.IndexOf('.'));
                        }
                        second = Convert.ToInt32(secondString);
                    }
                    catch{
                        throw new Exception("Invalid date value '" + date + "', invalid second value !");
                    }
                }

                s.ReadToFirstChar();
                if(s.Available > 3){
                    string timezone = s.SourceString.Replace(":","");
                    if(timezone.StartsWith("+") || timezone.StartsWith("-")){
                        bool utc_add_time = timezone.StartsWith("+");

                        // Remove +/- sign
                        timezone = timezone.Substring(1);

                        // padd time zone to 4 symbol. For example 200, will be 0200.
                        while(timezone.Length < 4){
                            timezone = "0" + timezone;
                        }

                        try{
                            // time zone format hours|minutes
                            int h = Convert.ToInt32(timezone.Substring(0,2));
                            int m = Convert.ToInt32(timezone.Substring(2));

                            if(utc_add_time){
                                zoneMinutes = 0 - ((h * 60) + m);
                            }
                            else{
                                zoneMinutes = (h * 60) + m;
                            }
                        }
                        catch{ // Just skip time zone, if can't parse
                        }
                    }
                }
            }
            //---------------------------------------------------------------------------------//

            // Convert time to UTC
            if(hour != -1 && minute != -1 && second != -1){
                DateTime d = new DateTime(year,month,day,hour,minute,second).AddMinutes(zoneMinutes);
                return new DateTime(d.Year,d.Month,d.Day,d.Hour,d.Minute,d.Second,DateTimeKind.Utc).ToLocalTime();
            }
            else{
                return new DateTime(year,month,day);
            }
        }
コード例 #18
0
        private void SEARCH(bool uid,string cmdTag,string cmdText)
        {
            /* 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}
                            C: XXXXXX
                            S: * SEARCH 43
                            S: A284 OK SEARCH completed
            */

            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;
            }

            // Store start time
			long startTime = DateTime.Now.Ticks;

            #region Parse arguments

            _CmdReader cmdReader = new _CmdReader(this,cmdText,Encoding.UTF8);            
            cmdReader.Start();

            StringReader r = new StringReader(cmdReader.CmdLine);
            
            // See if we have optional CHARSET argument.
            if(r.StartsWith("CHARSET",false)){
                r.ReadWord();

                string charset = r.ReadWord();
                if(!(string.Equals(charset,"US-ASCII",StringComparison.InvariantCultureIgnoreCase) || string.Equals(charset,"UTF-8",StringComparison.InvariantCultureIgnoreCase))){
                    m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus(cmdTag,"NO",new IMAP_t_orc_BadCharset(new string[]{"US-ASCII","UTF-8"}),"Not supported charset."));

                    return;
                }
            }

            #endregion

            try{
                IMAP_Search_Key_Group criteria = IMAP_Search_Key_Group.Parse(r);

                UpdateSelectedFolderAndSendChanges();
                
                List<int> matchedValues = new List<int>();

                IMAP_e_Search searchArgs = new IMAP_e_Search(criteria,new IMAP_r_ServerStatus(cmdTag,"OK","SEARCH completed in %exectime seconds."));
                searchArgs.Matched += new EventHandler<EventArgs<long>>(delegate(object s,EventArgs<long> e){
                    if(uid){
                        matchedValues.Add((int)e.Value);
                    }
                    else{
                        // Search sequence-number for that message.
                        int seqNo = m_pSelectedFolder.GetSeqNo(e.Value);
                        if(seqNo != -1){
                            matchedValues.Add((int)e.Value);
                        }
                    }                    
                });
                OnSearch(searchArgs);

                m_pResponseSender.SendResponseAsync(new IMAP_r_u_Search(matchedValues.ToArray()));
                m_pResponseSender.SendResponseAsync(IMAP_r_ServerStatus.Parse(searchArgs.Response.ToString().TrimEnd().Replace("%exectime",((DateTime.Now.Ticks - startTime) / (decimal)10000000).ToString("f2"))));
            }
            catch{
                m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus(cmdTag,"BAD","Error in arguments."));
            }            
        }
コード例 #19
0
        /// <summary>
        /// Gets specified message from server and stores to specified stream.
        /// </summary>
        /// <param name="uid">Message UID which to get.</param>
        /// <param name="storeStream">Stream where to store message.</param>
        public void FetchMessage(int uid,Stream storeStream)
        {
            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 !");
            }

            m_pSocket.WriteLine("a1 UID FETCH " + uid + " BODY[]");

            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 server response
                    reply = m_pSocket.ReadLine(50000);
                    continue;
                }

                reply = RemoveCmdTag(reply);

                // We must get here: BODY[] {sizeOfData}
                if(reply.ToUpper().ToString().IndexOf("BODY[") > - 1){
                    if(reply.IndexOf('{') > -1){
                        StringReader r = new StringReader(reply);
                        while(r.Available > 0 && !r.StartsWith("{")){
                            r.ReadSpecifiedLength(1);
                        }
                        int sizeOfData = Convert.ToInt32(r.ReadParenthesized());
                        m_pSocket.ReadSpecifiedLength(sizeOfData,storeStream);
                        m_pSocket.ReadLine();
                    }
                }

                // Read next 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);
                }
            }
        }