/// <summary>
        /// Updates current folder messages info with new messages info.
        /// </summary>
        /// <param name="folder"></param>
        /// <returns></returns>
        internal string Update(IMAP_SelectedFolder folder)
        {
            StringBuilder retVal = new StringBuilder();
            long          maxUID = this.MessageUidNext - 1;

            long countExists = this.Messages.Count;
            long countRecent = this.RecentCount;

            // Add new messages
            for (int i = folder.Messages.Count - 1; i > 0; i--)
            {
                IMAP_Message message = folder.Messages[i];
                // New message
                if (message.UID > maxUID)
                {
                    m_pMessages.Add(
                        message.ID,
                        message.UID,
                        message.InternalDate,
                        message.Size,
                        message.Flags
                        );
                }
                // New messages ended
                else
                {
                    break;
                }
            }

            // Remove deleted messages
            for (int i = 0; i < m_pMessages.Count - 1; i++)
            {
                IMAP_Message message = m_pMessages[i];

                if (!folder.m_pMessages.ContainsUID(message.UID))
                {
                    retVal.Append("* " + message.SequenceNo + " EXPUNGE\r\n");
                    m_pMessages.Remove(message);
                    i--;
                }
            }

            if (countExists != this.Messages.Count)
            {
                retVal.Append("* " + this.Messages.Count + " EXISTS\r\n");
            }
            if (countRecent != this.RecentCount)
            {
                retVal.Append("* " + this.RecentCount + " RECENT\r\n");
            }

            return(retVal.ToString());
        }
예제 #2
0
        /// <summary>
        /// Raises event 'GetMessagesInfo'.
        /// </summary>
        /// <param name="session">Reference to IMAP session.</param>
        /// <param name="folder">Folder which messages info to get.</param>
        /// <returns></returns>
        internal IMAP_eArgs_GetMessagesInfo OnGetMessagesInfo(IMAP_Session session, IMAP_SelectedFolder folder)
        {
            IMAP_eArgs_GetMessagesInfo eArgs = new IMAP_eArgs_GetMessagesInfo(session, folder);

            if (this.GetMessagesInfo != null)
            {
                this.GetMessagesInfo(session, eArgs);
            }

            return(eArgs);
        }
 /// <summary>
 /// Default constructor.
 /// </summary>
 public IMAP_eArgs_GetMessagesInfo(IMAP_Session session,IMAP_SelectedFolder folder)
 {
     m_pSession    = session;
     m_pFolderInfo = folder;
 }
예제 #4
0
        private void Examine(string cmdTag,string argsText)
        {
            /* Rfc 3501 6.3.2 EXAMINE Command

                Arguments:  mailbox name

                Responses:  REQUIRED untagged responses: FLAGS, EXISTS, RECENT
                            REQUIRED OK untagged responses:  UNSEEN,  PERMANENTFLAGS,
                            UIDNEXT, UIDVALIDITY

                Result:     OK - examine completed, now in selected state
                            NO - examine failure, now in authenticated state: no
                                    such mailbox, can't access mailbox
                            BAD - command unknown or arguments invalid

                The EXAMINE command is identical to SELECT and returns the same
                output; however, the selected mailbox is identified as read-only.
                No changes to the permanent state of the mailbox, including
                per-user state, are permitted; in particular, EXAMINE MUST NOT
                cause messages to lose the \Recent flag.

                The text of the tagged OK response to the EXAMINE command MUST
                begin with the "[READ-ONLY]" response code.

                Example:    C: A932 EXAMINE blurdybloop
                            S: * 17 EXISTS
                            S: * 2 RECENT
                            S: * OK [UNSEEN 8] Message 8 is first unseen
                            S: * OK [UIDVALIDITY 3857529045] UIDs valid
                            S: * OK [UIDNEXT 4392] Predicted next UID
                            S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
                            S: * OK [PERMANENTFLAGS ()] No permanent flags permitted
                            S: A932 OK [READ-ONLY] EXAMINE completed
            */
            if(!this.Authenticated){
                this.Socket.WriteLine(cmdTag + " NO Authenticate first !");
                return;
            }

            string[] args = TextUtils.SplitQuotedString(argsText,' ',true);
            if(args.Length != 1){
                this.Socket.WriteLine(cmdTag + " BAD EXAMINE invalid arguments. Syntax: {<command-tag> EXAMINE \"mailboxName\"}");
                return;
            }

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

            IMAP_SelectedFolder selectedFolder = new IMAP_SelectedFolder(Core.Decode_IMAP_UTF7_String(args[0]));
            IMAP_eArgs_GetMessagesInfo eArgs = m_pServer.OnGetMessagesInfo(this,selectedFolder);
            if(eArgs.ErrorText == null){
                m_pSelectedFolder = selectedFolder;
                m_pSelectedFolder.ReadOnly = true;
                m_SelectedMailbox = Core.Decode_IMAP_UTF7_String(args[0]);

                string response = "";
                response += "* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\r\n";
                response += "* " + m_pSelectedFolder.Messages.Count + " EXISTS\r\n";
                response += "* " + m_pSelectedFolder.RecentCount + " RECENT\r\n";
                response += "* OK [UNSEEN " + m_pSelectedFolder.FirstUnseen + "] Message " + m_pSelectedFolder.FirstUnseen + " is first unseen\r\n";
                response += "* OK [UIDVALIDITY " + m_pSelectedFolder.FolderUID + "] UIDs valid\r\n";
                response += "* OK [UIDNEXT " + m_pSelectedFolder.MessageUidNext + "] Predicted next UID\r\n";
                response += "* OK [PERMANENTFLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)] Available permanent falgs\r\n";
                response += cmdTag + " OK [READ-ONLY] EXAMINE  Completed in " + ((DateTime.Now.Ticks - startTime) / (decimal)10000000).ToString("f2") + " seconds\r\n";

                this.Socket.Write(response);
            }
            else{
                this.Socket.WriteLine(cmdTag + " NO " + eArgs.ErrorText);
            }
        }
예제 #5
0
        private void Close(string cmdTag)
        {
            /* RFC 3501 6.4.2 CLOSE Command

                Arguments:  none

                Responses:  no specific responses for this command

                Result:     OK - close completed, now in authenticated state
                            BAD - command unknown or arguments invalid

                The CLOSE command permanently removes from the currently selected
                mailbox all messages that have the \Deleted flag set, and returns
                to authenticated state from selected state.  No untagged EXPUNGE
                responses are sent.

                No messages are removed, and no error is given, if the mailbox is
                selected by an EXAMINE command or is otherwise selected read-only.

                Even if a mailbox is selected, a SELECT, EXAMINE, or LOGOUT
                command MAY be issued without previously issuing a CLOSE command.
                The SELECT, EXAMINE, and LOGOUT commands implicitly close the
                currently selected mailbox without doing an expunge.  However,
                when many messages are deleted, a CLOSE-LOGOUT or CLOSE-SELECT
                sequence is considerably faster than an EXPUNGE-LOGOUT or
                EXPUNGE-SELECT because no untagged EXPUNGE responses (which the
                client would probably ignore) are sent.

                Example:    C: A341 CLOSE
                            S: A341 OK CLOSE completed

            */
            if(!this.Authenticated){
                this.Socket.WriteLine(cmdTag + " NO Authenticate first !");
                return;
            }
            if(m_SelectedMailbox.Length == 0){
                this.Socket.WriteLine(cmdTag + " NO Select mailbox first !");
                return;
            }

            if(!m_pSelectedFolder.ReadOnly){
                IMAP_Message[] messages = m_pSelectedFolder.Messages.GetWithFlags(IMAP_MessageFlags.Deleted);
                foreach(IMAP_Message msg in messages){
                    m_pServer.OnDeleteMessage(this,msg);
                }
            }

            m_SelectedMailbox = "";
            m_pSelectedFolder = null;

            this.Socket.WriteLine(cmdTag + " OK CLOSE completed");
        }
예제 #6
0
        private void Status(string cmdTag,string argsText)
        {
            /* RFC 3501 6.3.10 STATUS Command

                Arguments:  mailbox name
                            status data item names

                Responses:  untagged responses: STATUS

                Result:     OK - status completed
                            NO - status failure: no status for that name
                            BAD - command unknown or arguments invalid

                The STATUS command requests the status of the indicated mailbox.
                It does not change the currently selected mailbox, nor does it
                affect the state of any messages in the queried mailbox (in
                particular, STATUS MUST NOT cause messages to lose the \Recent
                flag).

                The STATUS command provides an alternative to opening a second
                IMAP4rev1 connection and doing an EXAMINE command on a mailbox to
                query that mailbox's status without deselecting the current
                mailbox in the first IMAP4rev1 connection.

                Unlike the LIST command, the STATUS command is not guaranteed to
                be fast in its response.  In some implementations, the server is
                obliged to open the mailbox read-only internally to obtain certain
                status information.  Also unlike the LIST command, the STATUS
                command does not accept wildcards.

                The currently defined status data items that can be requested are:

                MESSAGES
                    The number of messages in the mailbox.

                RECENT
                    The number of messages with the \Recent flag set.

                UIDNEXT
                    The next unique identifier value of the mailbox.  Refer to
                    section 2.3.1.1 for more information.

                UIDVALIDITY
                    The unique identifier validity value of the mailbox.  Refer to
                    section 2.3.1.1 for more information.

                UNSEEN
                    The number of messages which do not have the \Seen flag set.

                Example:    C: A042 STATUS blurdybloop (UIDNEXT MESSAGES)
                            S: * STATUS blurdybloop (MESSAGES 231 UIDNEXT 44292)
                            S: A042 OK STATUS completed

            */
            if(!this.Authenticated){
                this.Socket.WriteLine(cmdTag + " NO Authenticate first !");
                return;
            }

            string[] args = ParseParams(argsText);
            if(args.Length != 2){
                this.Socket.WriteLine(cmdTag + " BAD Invalid STATUS arguments. Syntax: {<command-tag> STATUS \"<mailbox-name>\" \"(status-data-items)\"}");
                return;
            }

            string folder      = Core.Decode_IMAP_UTF7_String(args[0]);
            string wantedItems = args[1].ToUpper();

            // See wanted items are valid.
            if(wantedItems.Replace("MESSAGES","").Replace("RECENT","").Replace("UIDNEXT","").Replace("UIDVALIDITY","").Replace("UNSEEN","").Trim().Length > 0){
                this.Socket.WriteLine(cmdTag + " BAD STATUS invalid arguments");
                return;
            }

            IMAP_SelectedFolder selectedFolder = new IMAP_SelectedFolder(folder);
            IMAP_eArgs_GetMessagesInfo eArgs = m_pServer.OnGetMessagesInfo(this,selectedFolder);
            if(eArgs.ErrorText == null){
                string itemsReply = "";
                if(wantedItems.IndexOf("MESSAGES") > -1){
                    itemsReply += " MESSAGES " + selectedFolder.Messages.Count;
                }
                if(wantedItems.IndexOf("RECENT") > -1){
                    itemsReply += " RECENT " + selectedFolder.RecentCount;
                }
                if(wantedItems.IndexOf("UNSEEN") > -1){
                    itemsReply += " UNSEEN " + selectedFolder.UnSeenCount;
                }
                if(wantedItems.IndexOf("UIDVALIDITY") > -1){
                    itemsReply += " UIDVALIDITY " + selectedFolder.FolderUID;
                }
                if(wantedItems.IndexOf("UIDNEXT") > -1){
                    itemsReply += " UIDNEXT " + selectedFolder.MessageUidNext;
                }
                itemsReply = itemsReply.Trim();

                this.Socket.WriteLine("* STATUS " + args[0] + " (" + itemsReply + ")");
                this.Socket.WriteLine(cmdTag + " OK STATUS completed");
            }
            else{
                this.Socket.WriteLine(cmdTag + " NO " + eArgs.ErrorText);
            }
        }
예제 #7
0
        private void Select(string cmdTag,string argsText)
        {
            /* Rfc 3501 6.3.1 SELECT Command

                Arguments:  mailbox name

                Responses:  REQUIRED untagged responses: FLAGS, EXISTS, RECENT
                            REQUIRED OK untagged responses:  UNSEEN,  PERMANENTFLAGS,
                            UIDNEXT, UIDVALIDITY

                Result:     OK - select completed, now in selected state
                            NO - select failure, now in authenticated state: no
                                    such mailbox, can't access mailbox
                            BAD - command unknown or arguments invalid

                The SELECT command selects a mailbox so that messages in the
                mailbox can be accessed.  Before returning an OK to the client,
                the server MUST send the following untagged data to the client.
                Note that earlier versions of this protocol only required the
                FLAGS, EXISTS, and RECENT untagged data; consequently, client
                implementations SHOULD implement default behavior for missing data
                as discussed with the individual item.

                    FLAGS       Defined flags in the mailbox.  See the description
                                of the FLAGS response for more detail.

                    <n> EXISTS  The number of messages in the mailbox.  See the
                                description of the EXISTS response for more detail.

                    <n> RECENT  The number of messages with the \Recent flag set.
                                See the description of the RECENT response for more
                                detail.

                    OK [UNSEEN <n>]
                                The message sequence number of the first unseen
                                message in the mailbox.  If this is missing, the
                                client can not make any assumptions about the first
                                unseen message in the mailbox, and needs to issue a
                                SEARCH command if it wants to find it.

                    OK [PERMANENTFLAGS (<list of flags>)]
                                A list of message flags that the client can change
                                permanently.  If this is missing, the client should
                                assume that all flags can be changed permanently.

                    OK [UIDNEXT <n>]
                                The next unique identifier value.  Refer to section
                                2.3.1.1 for more information.  If this is missing,
                                the client can not make any assumptions about the
                                next unique identifier value.

                    OK [UIDVALIDITY <n>]
                     The unique identifier validity value.  Refer to
                     section 2.3.1.1 for more information.  If this is
                     missing, the server does not support unique
                     identifiers.

                Only one mailbox can be selected at a time in a connection;
                simultaneous access to multiple mailboxes requires multiple
                connections.  The SELECT command automatically deselects any
                currently selected mailbox before attempting the new selection.
                Consequently, if a mailbox is selected and a SELECT command that
                fails is attempted, no mailbox is selected.

                If the client is permitted to modify the mailbox, the server
                SHOULD prefix the text of the tagged OK response with the
                "[READ-WRITE]" response code.

                If the client is not permitted to modify the mailbox but is
                permitted read access, the mailbox is selected as read-only, and
                the server MUST prefix the text of the tagged OK response to
                SELECT with the "[READ-ONLY]" response code.  Read-only access
                through SELECT differs from the EXAMINE command in that certain
                read-only mailboxes MAY permit the change of permanent state on a
                per-user (as opposed to global) basis.  Netnews messages marked in
                a server-based .newsrc file are an example of such per-user
                permanent state that can be modified with read-only mailboxes.

                Example:    C: A142 SELECT INBOX
                            S: * 172 EXISTS
                            S: * 1 RECENT
                            S: * OK [UNSEEN 12] Message 12 is first unseen
                            S: * OK [UIDVALIDITY 3857529045] UIDs valid
                            S: * OK [UIDNEXT 4392] Predicted next UID
                            S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
                            S: * OK [PERMANENTFLAGS (\Deleted \Seen \*)] Limited
                            S: A142 OK [READ-WRITE] SELECT completed

            */
            if(!this.Authenticated){
                this.Socket.WriteLine(cmdTag + " NO Authenticate first !");
                return;
            }

            string[] args = TextUtils.SplitQuotedString(argsText,' ',true);
            if(args.Length != 1){
                this.Socket.WriteLine(cmdTag + " BAD SELECT invalid arguments. Syntax: {<command-tag> SELECT \"mailboxName\"}");
                return;
            }

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

            IMAP_SelectedFolder selectedFolder = new IMAP_SelectedFolder(Core.Decode_IMAP_UTF7_String(args[0]));
            IMAP_eArgs_GetMessagesInfo eArgs = m_pServer.OnGetMessagesInfo(this,selectedFolder);
            if(eArgs.ErrorText == null){
                m_pSelectedFolder = selectedFolder;
                m_SelectedMailbox = Core.Decode_IMAP_UTF7_String(args[0]);

                string response = "";
                response += "* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\r\n";
                response += "* " + m_pSelectedFolder.Messages.Count + " EXISTS\r\n";
                response += "* " + m_pSelectedFolder.RecentCount + " RECENT\r\n";
                response += "* OK [UNSEEN " + m_pSelectedFolder.FirstUnseen + "] Message " + m_pSelectedFolder.FirstUnseen + " is first unseen\r\n";
                response += "* OK [UIDVALIDITY " + m_pSelectedFolder.FolderUID + "] Folder UID\r\n";
                response += "* OK [UIDNEXT " + m_pSelectedFolder.MessageUidNext + "] Predicted next message UID\r\n";
                response += "* OK [PERMANENTFLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)] Available permanent flags\r\n";
                response += cmdTag + " OK [" + (m_pSelectedFolder.ReadOnly ? "READ-ONLY" : "READ-WRITE") + "] SELECT Completed in " + ((DateTime.Now.Ticks - startTime) / (decimal)10000000).ToString("f2") + " seconds\r\n";

                this.Socket.Write(response);
            }
            else{
                this.Socket.WriteLine(cmdTag + " NO " + eArgs.ErrorText);
            }
        }
예제 #8
0
        /// <summary>
        /// Processes changes and sends status responses if there are changes in selected mailbox.
        /// </summary>
        private void ProcessMailboxChanges()
        {
            // Get status
            IMAP_SelectedFolder        folderInfo = new IMAP_SelectedFolder(m_SelectedMailbox);
            IMAP_eArgs_GetMessagesInfo eArgs      = m_pServer.OnGetMessagesInfo(this,folderInfo);

            // Join new info with exisiting
            string statusResponse = m_pSelectedFolder.Update(folderInfo);
            if(!string.IsNullOrEmpty(statusResponse)){
                this.Socket.WriteLine(statusResponse);

                m_pSelectedFolder = folderInfo;
            }
        }
예제 #9
0
        /// <summary>
        /// Updates current folder messages info with new messages info.
        /// </summary>
        /// <param name="folder"></param>
        /// <returns></returns>
        internal string Update(IMAP_SelectedFolder folder)
        {
            StringBuilder retVal = new StringBuilder();
            long          maxUID = this.MessageUidNext - 1;

            long countExists = this.Messages.Count;
            long countRecent = this.RecentCount;

            // Add new messages
            for(int i=folder.Messages.Count-1;i>0;i--){
                IMAP_Message message = folder.Messages[i];
                // New message
                if(message.UID > maxUID){
                    m_pMessages.Add(
                        message.ID,
                        message.UID,
                        message.InternalDate,
                        message.Size,
                        message.Flags
                    );
                }
                // New messages ended
                else{
                    break;
                }
            }

            // Remove deleted messages
            for(int i=0;i<m_pMessages.Count-1;i++){
                IMAP_Message message = m_pMessages[i];

                if(!folder.m_pMessages.ContainsUID(message.UID)){
                    retVal.Append("* " + message.SequenceNo + " EXPUNGE\r\n");
                    m_pMessages.Remove(message);
                    i--;
                }
            }

            if(countExists != this.Messages.Count){
                retVal.Append("* " + this.Messages.Count + " EXISTS\r\n");
            }
            if(countRecent != this.RecentCount){
                retVal.Append("* " + this.RecentCount + " RECENT\r\n");
            }

            return retVal.ToString();
        }
예제 #10
0
 /// <summary>
 /// Default constructor.
 /// </summary>
 public IMAP_eArgs_GetMessagesInfo(IMAP_Session session, IMAP_SelectedFolder folder)
 {
     m_pSession    = session;
     m_pFolderInfo = folder;
 }
        private void Noop(string cmdTag)
        {
            /* RFC 3501 6.1.2 NOOP Command

                Arguments:  none

                Responses:  no specific responses for this command (but see below)

                Result:     OK - noop completed
                            BAD - command unknown or arguments invalid

                The NOOP command always succeeds.  It does nothing.
                Since any command can return a status update as untagged data, the
                NOOP command can be used as a periodic poll for new messages or
                message status updates during a period of inactivity.  The NOOP
                command can also be used to reset any inactivity autologout timer
                on the server.

                Example: C: a002 NOOP
                         S: a002 OK NOOP completed
            */

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

            // If there is selected mailbox, see if messages status has changed
            if(m_SelectedMailbox.Length > 0){
                // Get status
                IMAP_SelectedFolder        folderInfo = new IMAP_SelectedFolder(m_SelectedMailbox);
                IMAP_eArgs_GetMessagesInfo eArgs      = m_pServer.OnGetMessagesInfo(this,folderInfo);

                // Join new info with exisiting
                string statusResponse = m_pSelectedFolder.Update(folderInfo);

                this.Socket.WriteLine(statusResponse + cmdTag + " OK NOOP Completed in " + ((DateTime.Now.Ticks - startTime) / (decimal)10000000).ToString("f2") + " seconds\r\n");
            }
            else{
                this.Socket.WriteLine(cmdTag + " OK NOOP Completed in " + ((DateTime.Now.Ticks - startTime) / (decimal)10000000).ToString("f2") + " seconds\r\n");
            }
        }