/// <summary>
        /// Returns parsed IMAP SEARCH <b>SENTBEFORE (string)</b> key.
        /// </summary>
        /// <param name="r">String reader.</param>
        /// <returns>Returns parsed IMAP SEARCH <b>SENTBEFORE (string)</b> key.</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_SentBefore Parse(StringReader r)
        {
            if (r == null)
            {
                throw new ArgumentNullException("r");
            }

            string word = r.ReadWord();

            if (!string.Equals(word, "SENTBEFORE", StringComparison.InvariantCultureIgnoreCase))
            {
                throw new ParseException("Parse error: Not a SEARCH 'SENTBEFORE' key.");
            }
            string value = r.ReadWord();

            if (value == null)
            {
                throw new ParseException("Parse error: Invalid 'SENTBEFORE' value.");
            }
            DateTime date;

            try{
                date = IMAP_Utils.ParseDate(value);
            }
            catch {
                throw new ParseException("Parse error: Invalid 'SENTBEFORE' value.");
            }

            return(new IMAP_Search_Key_SentBefore(date));
        }
        /// <summary>
        /// Returns parsed IMAP SEARCH <b>SENTSINCE (string)</b> key.
        /// </summary>
        /// <param name="r">String reader.</param>
        /// <returns>Returns parsed IMAP SEARCH <b>SENTSINCE (string)</b> key.</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_SentSince Parse(StringReader r)
        {
            if (r == null)
            {
                throw new ArgumentNullException("r");
            }

            string word = r.ReadWord();

            if (!string.Equals(word, "SENTSINCE", Helpers.GetDefaultIgnoreCaseComparison()))
            {
                throw new ParseException("Parse error: Not a SEARCH 'SENTSINCE' key.");
            }
            string value = r.ReadWord();

            if (value == null)
            {
                throw new ParseException("Parse error: Invalid 'SENTSINCE' value.");
            }
            DateTime date;

            try{
                date = IMAP_Utils.ParseDate(value);
            }
            catch {
                throw new ParseException("Parse error: Invalid 'SENTSINCE' value.");
            }

            return(new IMAP_Search_Key_SentSince(date));
        }
Beispiel #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));
        }
        /// <summary>
        /// Parses IMAP FETCH ENVELOPE data-item.
        /// </summary>
        /// <param name="fetchReader">Fetch reader.</param>
        /// <returns>Returns parsed IMAP FETCH ENVELOPE data-item.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>fetchReader</b> is null reference value.</exception>
        internal static IMAP_Envelope Parse(IMAP_Client._FetchResponseReader fetchReader)
        {
            if (fetchReader == null)
            {
                throw new ArgumentNullException("fetchReader");
            }

            /* RFC 3501 7.4.2 ENVELOPE.
             *  A parenthesized list that describes the envelope structure of a
             *  message.  This is computed by the server by parsing the
             *  [RFC-2822] header into the component parts, defaulting various
             *  fields as necessary.
             *
             *  The fields of the envelope structure are in the following
             *  order: date, subject, from, sender, reply-to, to, cc, bcc,
             *  in-reply-to, and message-id.  The date, subject, in-reply-to,
             *  and message-id fields are strings.  The from, sender, reply-to,
             *  to, cc, and bcc fields are parenthesized lists of address
             *  structures.
             *
             *  An address structure is a parenthesized list that describes an
             *  electronic mail address.  The fields of an address structure
             *  are in the following order: personal name, [SMTP]
             *  at-domain-list (source route), mailbox name, and host name.
             *
             *  [RFC-2822] group syntax is indicated by a special form of
             *  address structure in which the host name field is NIL.  If the
             *  mailbox name field is also NIL, this is an end of group marker
             *  (semi-colon in RFC 822 syntax).  If the mailbox name field is
             *  non-NIL, this is a start of group marker, and the mailbox name
             *  field holds the group name phrase.
             *
             *  If the Date, Subject, In-Reply-To, and Message-ID header lines
             *  are absent in the [RFC-2822] header, the corresponding member
             *  of the envelope is NIL; if these header lines are present but
             *  empty the corresponding member of the envelope is the empty
             *  string.
             *
             *      Note: some servers may return a NIL envelope member in the
             *      "present but empty" case.  Clients SHOULD treat NIL and
             *      empty string as identical.
             *
             *      Note: [RFC-2822] requires that all messages have a valid
             *      Date header.  Therefore, the date member in the envelope can
             *      not be NIL or the empty string.
             *
             *      Note: [RFC-2822] requires that the In-Reply-To and
             *      Message-ID headers, if present, have non-empty content.
             *      Therefore, the in-reply-to and message-id members in the
             *      envelope can not be the empty string.
             *
             *  If the From, To, cc, and bcc header lines are absent in the
             *  [RFC-2822] header, or are present but empty, the corresponding
             *  member of the envelope is NIL.
             *
             *  If the Sender or Reply-To lines are absent in the [RFC-2822]
             *  header, or are present but empty, the server sets the
             *  corresponding member of the envelope to be the same value as
             *  the from member (the client is not expected to know to do
             *  this).
             *
             *      Note: [RFC-2822] requires that all messages have a valid
             *      From header.  Therefore, the from, sender, and reply-to
             *      members in the envelope can not be NIL.
             */

            // Eat "ENVELOPE".
            fetchReader.GetReader().ReadWord();
            fetchReader.GetReader().ReadToFirstChar();
            // Eat starting "(".
            fetchReader.GetReader().ReadSpecifiedLength(1);

            // Read "date".
            DateTime date  = DateTime.MinValue;
            string   dateS = fetchReader.ReadString();

            if (dateS != null)
            {
                date = IMAP_Utils.ParseDate(dateS);
            }

            // Read "subject".
            string subject = fetchReader.ReadString();

            // Read "from"
            Mail_t_Address[] from = ReadAddresses(fetchReader);

            //Read "sender"
            Mail_t_Address[] sender = ReadAddresses(fetchReader);

            // Read "reply-to"
            Mail_t_Address[] replyTo = ReadAddresses(fetchReader);

            // Read "to"
            Mail_t_Address[] to = ReadAddresses(fetchReader);

            // Read "cc"
            Mail_t_Address[] cc = ReadAddresses(fetchReader);

            // Read "bcc"
            Mail_t_Address[] bcc = ReadAddresses(fetchReader);

            // Read "in-reply-to"
            string inReplyTo = fetchReader.ReadString();

            // Read "message-id"
            string messageID = fetchReader.ReadString();

            // Eat ending ")".
            fetchReader.GetReader().ReadToFirstChar();
            fetchReader.GetReader().ReadSpecifiedLength(1);

            return(new IMAP_Envelope(date, subject, from, sender, replyTo, to, cc, bcc, inReplyTo, messageID));
        }