/// <summary> /// Cleans up any resource being used. /// </summary> public override void Dispose() { if(this.IsDisposed){ return; } base.Dispose(); m_pAuthentications = null; m_pCapabilities = null; m_pUser = null; m_pSelectedFolder = null; if(m_pResponseSender != null){ m_pResponseSender.Dispose(); } // Release events this.Started = null; this.Login = null; this.Namespace = null; this.List = null; this.Create = null; this.Delete = null; this.Rename = null; this.LSub = null; this.Subscribe = null; this.Unsubscribe = null; this.Select = null; this.GetMessagesInfo = null; this.Append = null; this.GetQuotaRoot = null; this.GetQuota = null; this.GetAcl = null; this.SetAcl = null; this.DeleteAcl = null; this.ListRights = null; this.MyRights = null; this.Fetch = null; this.Search = null; this.Store = null; this.Copy = null; this.Expunge = null; }
/// <summary> /// Updates current slected folder status and sends currently selected folder changes versus current folder state. /// </summary> private void UpdateSelectedFolderAndSendChanges() { if(m_pSelectedFolder == null){ return; } IMAP_e_MessagesInfo e = OnGetMessagesInfo(m_pSelectedFolder.Folder); int currentExists = m_pSelectedFolder.MessagesInfo.Length; // Create ID indexed lookup table for new messages. Dictionary<string,string> newMessagesLookup = new Dictionary<string,string>(); foreach(IMAP_MessageInfo msgInfo in e.MessagesInfo){ newMessagesLookup.Add(msgInfo.ID,null); } StringBuilder retVal = new StringBuilder(); // Check deleted messages, send "* n EXPUNGE" for each deleted message. foreach(IMAP_MessageInfo msgInfo in m_pSelectedFolder.MessagesInfo){ // Message deleted. if(!newMessagesLookup.ContainsKey(msgInfo.ID)){ retVal.Append("* " + m_pSelectedFolder.GetSeqNo(msgInfo) + " EXPUNGE\r\n"); m_pSelectedFolder.RemoveMessage(msgInfo); } } // Send EXISTS if current count differs from existing. if(currentExists != e.MessagesInfo.Count){ retVal.Append("* " + e.MessagesInfo.Count + " EXISTS\r\n"); } // Send STATUS change responses. if(retVal.Length > 0){ WriteLine(retVal.ToString()); } // Create new selected folder based on new messages info. m_pSelectedFolder = new _SelectedFolder(m_pSelectedFolder.Folder,m_pSelectedFolder.IsReadOnly,e.MessagesInfo); }
private void EXAMINE(string cmdTag,string cmdText) { /* 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 */ /* 5738 3.2. UTF8 Parameter to SELECT and EXAMINE The "UTF8=ACCEPT" capability also indicates that the server supports the "UTF8" parameter to SELECT and EXAMINE. When a mailbox is selected with the "UTF8" parameter, it alters the behavior of all IMAP commands related to message sizes, message headers, and MIME body headers so they refer to the message with UTF-8 headers. If the mailstore is not UTF-8 header native and the SELECT or EXAMINE command with UTF-8 header modifier succeeds, then the server MUST return results as if the mailstore were UTF-8 header native with upconversion requirements as described in Section 8. The server MAY reject the SELECT or EXAMINE command with the [NOT-UTF-8] response code, unless the "UTF8=ALL" or "UTF8=ONLY" capability is advertised. Servers MAY include mailboxes that can only be selected or examined if the "UTF8" parameter is provided. However, such mailboxes MUST NOT be included in the output of an unextended LIST, LSUB, or equivalent command. If a client attempts to SELECT or EXAMINE such mailboxes without the "UTF8" parameter, the server MUST reject the command with a [UTF-8-ONLY] response code. As a result, such mailboxes will not be accessible by IMAP clients written prior to this specification and are discouraged unless the server advertises "UTF8=ONLY" or the server implements IMAP4 LIST Command Extensions utf8-select-param = "UTF8" ;; Conforms to <select-param> from RFC 4466 C: a SELECT newmailbox (UTF8) S: ... S: a OK SELECT completed C: b FETCH 1 (SIZE ENVELOPE BODY) S: ... < UTF-8 header native results > S: b OK FETCH completed C: c EXAMINE legacymailbox (UTF8) S: c NO [NOT-UTF-8] Mailbox does not support UTF-8 access */ if(!this.IsAuthenticated){ m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus(cmdTag,"NO","Authentication required.")); return; } // Store start time long startTime = DateTime.Now.Ticks; // Unselect folder if any selected. if(m_pSelectedFolder != null){ m_pSelectedFolder = null; } string[] args = TextUtils.SplitQuotedString(cmdText,' '); if(args.Length >= 2){ // At moment we don't support UTF-8 mailboxes. if(string.Equals(args[1],"(UTF8)",StringComparison.InvariantCultureIgnoreCase)){ m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus(cmdTag,"NO",new IMAP_t_orc_Unknown("NOT-UTF-8"),"Mailbox does not support UTF-8 access.")); } else{ m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus(cmdTag,"BAD","Error in arguments.")); } return; } string folder = TextUtils.UnQuoteString(IMAP_Utils.DecodeMailbox(cmdText)); IMAP_e_Select e = OnSelect(cmdTag,folder); if(e.ErrorResponse == null){ IMAP_e_MessagesInfo eMessagesInfo = OnGetMessagesInfo(folder); m_pResponseSender.SendResponseAsync(new IMAP_r_u_Exists(eMessagesInfo.Exists)); m_pResponseSender.SendResponseAsync(new IMAP_r_u_Recent(eMessagesInfo.Recent)); if(eMessagesInfo.FirstUnseen > -1){ m_pResponseSender.SendResponseAsync(new IMAP_r_u_ServerStatus("OK",new IMAP_t_orc_Unseen(eMessagesInfo.FirstUnseen),"Message " + eMessagesInfo.FirstUnseen + " is the first unseen.")); } m_pResponseSender.SendResponseAsync(new IMAP_r_u_ServerStatus("OK",new IMAP_t_orc_UidNext((int)eMessagesInfo.UidNext),"Predicted next message UID.")); m_pResponseSender.SendResponseAsync(new IMAP_r_u_ServerStatus("OK",new IMAP_t_orc_UidValidity(e.FolderUID),"Folder UID value.")); m_pResponseSender.SendResponseAsync(new IMAP_r_u_Flags(e.Flags.ToArray())); m_pResponseSender.SendResponseAsync(new IMAP_r_u_ServerStatus("OK",new IMAP_t_orc_PermanentFlags(e.PermanentFlags.ToArray()),"Avaliable permanent flags.")); m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus(cmdTag,"OK",new IMAP_t_orc_ReadOnly(),"EXAMINE completed in " + ((DateTime.Now.Ticks - startTime) / (decimal)10000000).ToString("f2") + " seconds.")); m_pSelectedFolder = new _SelectedFolder(folder,e.IsReadOnly,eMessagesInfo.MessagesInfo); m_pSelectedFolder.Reindex(); } else{ m_pResponseSender.SendResponseAsync(e.ErrorResponse); } }
private void CLOSE(string cmdTag,string cmdText) { /* 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 all messages that have the \Deleted flag set from the currently selected mailbox, and returns to the authenticated state from the 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.IsAuthenticated){ m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus(cmdTag,"NO","Authentication required.")); return; } if(m_pSelectedFolder == null){ m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus(cmdTag,"NO","Error: This command is valid only in selected state.")); return; } if(m_pSelectedFolder != null && !m_pSelectedFolder.IsReadOnly){ foreach(IMAP_MessageInfo msgInfo in m_pSelectedFolder.MessagesInfo){ if(msgInfo.ContainsFlag("Deleted")){ OnExpunge(msgInfo,new IMAP_r_ServerStatus("dummy","OK","This is CLOSE command expunge, so this response is not used.")); } } } m_pSelectedFolder = null; m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus(cmdTag,"OK","CLOSE completed.")); }
private void SELECT(string cmdTag,string cmdText) { /* 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 */ /* 5738 3.2. UTF8 Parameter to SELECT and EXAMINE The "UTF8=ACCEPT" capability also indicates that the server supports the "UTF8" parameter to SELECT and EXAMINE. When a mailbox is selected with the "UTF8" parameter, it alters the behavior of all IMAP commands related to message sizes, message headers, and MIME body headers so they refer to the message with UTF-8 headers. If the mailstore is not UTF-8 header native and the SELECT or EXAMINE command with UTF-8 header modifier succeeds, then the server MUST return results as if the mailstore were UTF-8 header native with upconversion requirements as described in Section 8. The server MAY reject the SELECT or EXAMINE command with the [NOT-UTF-8] response code, unless the "UTF8=ALL" or "UTF8=ONLY" capability is advertised. Servers MAY include mailboxes that can only be selected or examined if the "UTF8" parameter is provided. However, such mailboxes MUST NOT be included in the output of an unextended LIST, LSUB, or equivalent command. If a client attempts to SELECT or EXAMINE such mailboxes without the "UTF8" parameter, the server MUST reject the command with a [UTF-8-ONLY] response code. As a result, such mailboxes will not be accessible by IMAP clients written prior to this specification and are discouraged unless the server advertises "UTF8=ONLY" or the server implements IMAP4 LIST Command Extensions utf8-select-param = "UTF8" ;; Conforms to <select-param> from RFC 4466 C: a SELECT newmailbox (UTF8) S: ... S: a OK SELECT completed C: b FETCH 1 (SIZE ENVELOPE BODY) S: ... < UTF-8 header native results > S: b OK FETCH completed C: c EXAMINE legacymailbox (UTF8) S: c NO [NOT-UTF-8] Mailbox does not support UTF-8 access */ // Store start time long startTime = DateTime.Now.Ticks; if(!this.IsAuthenticated){ m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus(cmdTag,"NO","Authentication required.")); return; } // Unselect folder if any selected. if(m_pSelectedFolder != null){ m_pSelectedFolder = null; } string[] args = TextUtils.SplitQuotedString(cmdText,' '); if(args.Length >= 2){ // At moment we don't support UTF-8 mailboxes. if(string.Equals(args[1],"(UTF8)",StringComparison.InvariantCultureIgnoreCase)){ m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus(cmdTag,"NO",new IMAP_t_orc_Unknown("NOT-UTF-8"),"Mailbox does not support UTF-8 access.")); } else{ m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus(cmdTag,"BAD","Error in arguments.")); } return; } try{ string folder = TextUtils.UnQuoteString(IMAP_Utils.DecodeMailbox(cmdText)); IMAP_e_Select e = OnSelect(cmdTag,folder); if(e.ErrorResponse == null){ IMAP_e_MessagesInfo eMessagesInfo = OnGetMessagesInfo(folder); m_pResponseSender.SendResponseAsync(new IMAP_r_u_Exists(eMessagesInfo.Exists)); m_pResponseSender.SendResponseAsync(new IMAP_r_u_Recent(eMessagesInfo.Recent)); if(eMessagesInfo.FirstUnseen > -1){ m_pResponseSender.SendResponseAsync(new IMAP_r_u_ServerStatus("OK",new IMAP_t_orc_Unseen(eMessagesInfo.FirstUnseen),"Message " + eMessagesInfo.FirstUnseen + " is the first unseen.")); } m_pResponseSender.SendResponseAsync(new IMAP_r_u_ServerStatus("OK",new IMAP_t_orc_UidNext((int)eMessagesInfo.UidNext),"Predicted next message UID.")); m_pResponseSender.SendResponseAsync(new IMAP_r_u_ServerStatus("OK",new IMAP_t_orc_UidValidity(e.FolderUID),"Folder UID value.")); m_pResponseSender.SendResponseAsync(new IMAP_r_u_Flags(e.Flags.ToArray())); m_pResponseSender.SendResponseAsync(new IMAP_r_u_ServerStatus("OK",new IMAP_t_orc_PermanentFlags(e.PermanentFlags.ToArray()),"Avaliable permanent flags.")); m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus(cmdTag,"OK",new IMAP_t_orc_Unknown(e.IsReadOnly ? "READ-ONLY" : "READ-WRITE"),"SELECT completed in " + ((DateTime.Now.Ticks - startTime) / (decimal)10000000).ToString("f2") + " seconds.")); m_pSelectedFolder = new _SelectedFolder(folder,e.IsReadOnly,eMessagesInfo.MessagesInfo); m_pSelectedFolder.Reindex(); } else{ m_pResponseSender.SendResponseAsync(e.ErrorResponse); } } catch(Exception x){ m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus(cmdTag,"NO","NO Error: " + x.Message)); } }