Example #1
0
        /// <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));
        }
Example #2
0
        /// <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))
                {
                    // BODYSTRUCTURE can contain string literals, we just try to parse it.
                    // If parse fails, just get string literal and try again as long as all BODYSTRUCTURE data has read.

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

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

                        try{
                            bodyStructure = tmpReader.ReadParenthesized();
                            // We got full BODYSTRUCTURE, 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_BodyStructure.Parse(new StringReader(bodyStructure)));
                }

                #endregion

                #region BODYSTRUCTURE

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

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

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

                        try{
                            bodyStructure = tmpReader.ReadParenthesized();
                            // We got full BODYSTRUCTURE, 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_BodyStructure.Parse(new StringReader(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));
        }