/// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="flags">Message flags.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>flags</b> is null reference.</exception>
        public IMAP_t_Fetch_r_i_Flags(IMAP_t_MsgFlags flags)
        {
            if(flags == null){
                throw new ArgumentNullException("flags");
            }

            m_pFlags = flags;
        }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="flags">Message flags.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>flags</b> is null reference.</exception>
        public IMAP_t_Fetch_r_i_Flags(IMAP_t_MsgFlags flags)
        {
            if (flags == null)
            {
                throw new ArgumentNullException("flags");
            }

            m_pFlags = flags;
        }
Ejemplo n.º 3
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));
        }