public RegisterUntaggedHandler ( string atom, ImapUntaggedHandler handler ) : void | ||
atom | string | The atom token. |
handler | ImapUntaggedHandler | The handler. |
return | void |
/// <summary> /// Queries the namespaces. /// </summary> /// <returns>The command result.</returns> /// <param name="cancellationToken">The cancellation token.</param> public ImapCommandResult QueryNamespaces(CancellationToken cancellationToken) { if (stream == null) throw new InvalidOperationException (); ImapCommand ic; if ((Capabilities & ImapCapabilities.Namespace) != 0) { ic = QueueCommand (cancellationToken, null, "NAMESPACE\r\n"); Wait (ic); } else { var list = new List<ImapFolder> (); ic = new ImapCommand (this, cancellationToken, null, "LIST \"\" \"\"\r\n"); ic.RegisterUntaggedHandler ("LIST", ImapUtils.ParseFolderList); ic.UserData = list; QueueCommand (ic); Wait (ic); PersonalNamespaces.Clear (); SharedNamespaces.Clear (); OtherNamespaces.Clear (); if (list.Count > 0) { PersonalNamespaces.Add (new FolderNamespace (list[0].DirectorySeparator, "")); list[0].IsNamespace = true; } LookupParentFolders (list, cancellationToken); } return ic.Result; }
/// <summary> /// Searches the subset of UIDs in the folder for messages matching the specified query. /// </summary> /// <remarks> /// Searches the fsubset of UIDs in the folder for messages matching the specified query, /// returning only the specified search results. /// </remarks> /// <returns>The search results.</returns> /// <param name="options">The search options.</param> /// <param name="uids">The subset of UIDs</param> /// <param name="query">The search query.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="uids"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="query"/> is <c>null</c>.</para> /// </exception> /// <exception cref="System.ArgumentException"> /// One or more of the <paramref name="uids"/> is invalid. /// </exception> /// <exception cref="System.NotSupportedException"> /// <para>One or more search terms in the <paramref name="query"/> are not supported by the IMAP server.</para> /// <para>-or-</para> /// <para>The IMAP server does not support the ESEARCH extension.</para> /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="ServiceNotAuthenticatedException"> /// The <see cref="ImapClient"/> is not authenticated. /// </exception> /// <exception cref="FolderNotOpenException"> /// The <see cref="ImapFolder"/> is not currently open. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// The server's response contained unexpected tokens. /// </exception> /// <exception cref="ImapCommandException"> /// The server replied with a NO or BAD response. /// </exception> public override SearchResults Search (SearchOptions options, IList<UniqueId> uids, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)) { var set = ImapUtils.FormatUidSet (uids); var args = new List<string> (); string charset; if (query == null) throw new ArgumentNullException ("query"); CheckState (true, false); if ((Engine.Capabilities & ImapCapabilities.ESearch) == 0) throw new NotSupportedException ("The IMAP server does not support the ESEARCH extension."); if (uids.Count == 0) return new SearchResults (); var optimized = query.Optimize (new ImapSearchQueryOptimizer ()); var expr = BuildQueryExpression (optimized, args, out charset); var command = "UID SEARCH RETURN ("; if ((options & SearchOptions.Count) != 0) command += "COUNT "; if ((options & SearchOptions.Min) != 0) command += "MIN "; if ((options & SearchOptions.Max) != 0) command += "MAX "; command = command.TrimEnd (); command += ") "; if (args.Count > 0 && !Engine.UTF8Enabled) command += "CHARSET " + charset + " "; command += "UID " + set + " " + expr + "\r\n"; var ic = new ImapCommand (Engine, cancellationToken, this, command, args.ToArray ()); ic.RegisterUntaggedHandler ("ESEARCH", ESearchMatches); ic.UserData = new SearchResults (); Engine.QueueCommand (ic); Engine.Wait (ic); ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create ("SEARCH", ic); return (SearchResults) ic.UserData; }
/// <summary> /// Threads the messages in the folder that match the search query using the specified threading algorithm. /// </summary> /// <remarks> /// The <see cref="MessageThread.UniqueId"/> can be used with methods such as /// <see cref="IMailFolder.GetMessage(UniqueId,CancellationToken,ITransferProgress)"/>. /// </remarks> /// <returns>An array of message threads.</returns> /// <param name="uids">The subset of UIDs</param> /// <param name="algorithm">The threading algorithm to use.</param> /// <param name="query">The search query.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ArgumentOutOfRangeException"> /// <paramref name="algorithm"/> is not supported. /// </exception> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="uids"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="query"/> is <c>null</c>.</para> /// </exception> /// <exception cref="System.ArgumentException"> /// <para><paramref name="uids"/> is empty.</para> /// <para>-or-</para> /// <para>One or more of the <paramref name="uids"/> is invalid.</para> /// </exception> /// <exception cref="System.NotSupportedException"> /// <para>One or more search terms in the <paramref name="query"/> are not supported by the IMAP server.</para> /// <para>-or-</para> /// <para>The server does not support the THREAD extension.</para> /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="ServiceNotAuthenticatedException"> /// The <see cref="ImapClient"/> is not authenticated. /// </exception> /// <exception cref="FolderNotOpenException"> /// The <see cref="ImapFolder"/> is not currently open. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// The server's response contained unexpected tokens. /// </exception> /// <exception cref="ImapCommandException"> /// The server replied with a NO or BAD response. /// </exception> public override IList<MessageThread> Thread (IList<UniqueId> uids, ThreadingAlgorithm algorithm, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)) { var method = algorithm.ToString ().ToUpperInvariant (); var set = ImapUtils.FormatUidSet (uids); var args = new List<string> (); string charset; if ((Engine.Capabilities & ImapCapabilities.Thread) == 0) throw new NotSupportedException ("The IMAP server does not support the THREAD extension."); if (!Engine.ThreadingAlgorithms.Contains (algorithm)) throw new ArgumentOutOfRangeException ("algorithm", "The specified threading algorithm is not supported."); if (query == null) throw new ArgumentNullException ("query"); CheckState (true, false); var optimized = query.Optimize (new ImapSearchQueryOptimizer ()); var expr = BuildQueryExpression (optimized, args, out charset); var command = "UID THREAD " + method + " " + charset + " "; command += "UID " + set + " " + expr + "\r\n"; var ic = new ImapCommand (Engine, cancellationToken, this, command, args.ToArray ()); ic.RegisterUntaggedHandler ("THREAD", ThreadMatches); Engine.QueueCommand (ic); Engine.Wait (ic); ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create ("THREAD", ic); var threads = (IList<MessageThread>) ic.UserData; if (threads == null) return new MessageThread[0]; return threads; }
/// <summary> /// Gets a substream of the specified body part. /// </summary> /// <remarks> /// <para>Gets a substream of the specified message.</para> /// <para>For more information about how to construct the <paramref name="section"/>, /// see Section 6.4.5 of RFC3501.</para> /// </remarks> /// <returns>The stream.</returns> /// <param name="uid">The UID of the message.</param> /// <param name="section">The desired section of the message.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="progress">The progress reporting mechanism.</param> /// <exception cref="System.ArgumentException"> /// <paramref name="uid"/> is invalid. /// </exception> /// <exception cref="System.ArgumentNullException"> /// <paramref name="section"/> is <c>null</c>. /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="ServiceNotAuthenticatedException"> /// The <see cref="ImapClient"/> is not authenticated. /// </exception> /// <exception cref="FolderNotOpenException"> /// The <see cref="ImapFolder"/> is not currently open. /// </exception> /// <exception cref="MessageNotFoundException"> /// The IMAP server did not return the requested message stream. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// The server's response contained unexpected tokens. /// </exception> /// <exception cref="ImapCommandException"> /// The server replied with a NO or BAD response. /// </exception> public override Stream GetStream (UniqueId uid, string section, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) { if (uid.Id == 0) throw new ArgumentException ("The uid is invalid.", "uid"); if (section == null) throw new ArgumentNullException ("section"); CheckState (true, false); var command = string.Format ("UID FETCH {0} (BODY.PEEK[{1}])\r\n", uid.Id, section); var ic = new ImapCommand (Engine, cancellationToken, this, command); var ctx = new FetchStreamContext (progress); Stream stream; ic.RegisterUntaggedHandler ("FETCH", FetchStream); ic.UserData = ctx; Engine.QueueCommand (ic); try { Engine.Wait (ic); ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create ("FETCH", ic); if (!ctx.Sections.TryGetValue (section, out stream)) throw new MessageNotFoundException ("The IMAP server did not return the requested stream."); ctx.Sections.Remove (section); } finally { ctx.Dispose (); } return stream; }
/// <summary> /// Gets a substream of the specified message. /// </summary> /// <remarks> /// <para>Gets a substream of the specified message. If the starting offset is beyond /// the end of the specified section of the message, an empty stream is returned. If /// the number of bytes desired extends beyond the end of the section, a truncated /// stream will be returned.</para> /// <para>For more information about how to construct the <paramref name="section"/>, /// see Section 6.4.5 of RFC3501.</para> /// </remarks> /// <returns>The stream.</returns> /// <param name="index">The index of the message.</param> /// <param name="section">The desired section of the message.</param> /// <param name="offset">The starting offset of the first desired byte.</param> /// <param name="count">The number of bytes desired.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="progress">The progress reporting mechanism.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="section"/> is <c>null</c>. /// </exception> /// <exception cref="System.ArgumentOutOfRangeException"> /// <para><paramref name="index"/> is out of range.</para> /// <para>-or-</para> /// <para><paramref name="offset"/> is negative.</para> /// <para>-or-</para> /// <para><paramref name="count"/> is negative.</para> /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="ServiceNotAuthenticatedException"> /// The <see cref="ImapClient"/> is not authenticated. /// </exception> /// <exception cref="FolderNotOpenException"> /// The <see cref="ImapFolder"/> is not currently open. /// </exception> /// <exception cref="MessageNotFoundException"> /// The IMAP server did not return the requested message stream. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// The server's response contained unexpected tokens. /// </exception> /// <exception cref="ImapCommandException"> /// The server replied with a NO or BAD response. /// </exception> public override Stream GetStream (int index, string section, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException ("index"); if (section == null) throw new ArgumentNullException ("section"); if (offset < 0) throw new ArgumentOutOfRangeException ("offset"); if (count < 0) throw new ArgumentOutOfRangeException ("count"); CheckState (true, false); if (count == 0) return new MemoryStream (); var command = string.Format ("FETCH {0} (BODY.PEEK[{1}]<{2}.{3}>)\r\n", index + 1, section, offset, count); var ic = new ImapCommand (Engine, cancellationToken, this, command); var ctx = new FetchStreamContext (progress); Stream stream; ic.RegisterUntaggedHandler ("FETCH", FetchStream); ic.UserData = ctx; Engine.QueueCommand (ic); try { Engine.Wait (ic); ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create ("FETCH", ic); if (!ctx.Sections.TryGetValue (section, out stream)) throw new MessageNotFoundException ("The IMAP server did not return the requested stream."); ctx.Sections.Remove (section); } finally { ctx.Dispose (); } return stream; }
/// <summary> /// Creates a new subfolder with the given name. /// </summary> /// <remarks> /// Creates a new subfolder with the given name. /// </remarks> /// <returns>The created folder.</returns> /// <param name="name">The name of the folder to create.</param> /// <param name="isMessageFolder"><c>true</c> if the folder will be used to contain messages; otherwise <c>false</c>.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="name"/> is <c>null</c>. /// </exception> /// <exception cref="System.ArgumentException"> /// <paramref name="name"/> is empty. /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="ServiceNotAuthenticatedException"> /// The <see cref="ImapClient"/> is not authenticated. /// </exception> /// <exception cref="System.InvalidOperationException"> /// The <see cref="MailFolder.DirectorySeparator"/> is nil, and thus child folders cannot be created. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// The server's response contained unexpected tokens. /// </exception> /// <exception cref="ImapCommandException"> /// The server replied with a NO or BAD response. /// </exception> public override IMailFolder Create (string name, bool isMessageFolder, CancellationToken cancellationToken = default (CancellationToken)) { if (name == null) throw new ArgumentNullException ("name"); if (!Engine.IsValidMailboxName (name, DirectorySeparator)) throw new ArgumentException ("The name is not a legal folder name.", "name"); CheckState (false, false); if (!string.IsNullOrEmpty (FullName) && DirectorySeparator == '\0') throw new InvalidOperationException ("Cannot create child folders."); var fullName = !string.IsNullOrEmpty (FullName) ? FullName + DirectorySeparator + name : name; var encodedName = Engine.EncodeMailboxName (fullName); var list = new List<ImapFolder> (); var createName = encodedName; ImapFolder folder; if (!isMessageFolder) createName += DirectorySeparator; var ic = Engine.QueueCommand (cancellationToken, null, "CREATE %S\r\n", createName); Engine.Wait (ic); ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create ("CREATE", ic); ic = new ImapCommand (Engine, cancellationToken, null, "LIST \"\" %S\r\n", encodedName); ic.RegisterUntaggedHandler ("LIST", ImapUtils.ParseFolderList); ic.UserData = list; Engine.QueueCommand (ic); Engine.Wait (ic); ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create ("LIST", ic); if ((folder = list.FirstOrDefault ()) != null) folder.ParentFolder = this; return folder; }
/// <summary> /// Gets the specified body part. /// </summary> /// <remarks> /// Gets the specified body part. /// </remarks> /// <returns>The body part.</returns> /// <param name="index">The index of the message.</param> /// <param name="partSpecifier">The body part specifier.</param> /// <param name="headersOnly"><c>true</c> if only the headers should be downloaded; otherwise, <c>false</c>></param> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="progress">The progress reporting mechanism.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="partSpecifier"/> is <c>null</c>. /// </exception> /// <exception cref="System.ArgumentOutOfRangeException"> /// <paramref name="index"/> is out of range. /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="ServiceNotAuthenticatedException"> /// The <see cref="ImapClient"/> is not authenticated. /// </exception> /// <exception cref="FolderNotOpenException"> /// The <see cref="ImapFolder"/> is not currently open. /// </exception> /// <exception cref="MessageNotFoundException"> /// The IMAP server did not return the requested message. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// The server's response contained unexpected tokens. /// </exception> /// <exception cref="ImapCommandException"> /// The server replied with a NO or BAD response. /// </exception> public MimeEntity GetBodyPart (int index, string partSpecifier, bool headersOnly, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException ("index"); if (partSpecifier == null) throw new ArgumentNullException ("partSpecifier"); CheckState (true, false); string[] tags; var command = string.Format ("FETCH {0} ({1})\r\n", index + 1, GetBodyPartQuery (partSpecifier, headersOnly, out tags)); var ic = new ImapCommand (Engine, cancellationToken, this, command); var ctx = new FetchStreamContext (progress); ChainedStream chained; bool dispose = false; Stream stream; ic.RegisterUntaggedHandler ("FETCH", FetchStream); ic.UserData = ctx; Engine.QueueCommand (ic); try { Engine.Wait (ic); ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create ("FETCH", ic); chained = new ChainedStream (); foreach (var tag in tags) { if (!ctx.Sections.TryGetValue (tag, out stream)) throw new MessageNotFoundException ("The IMAP server did not return the requested body part."); if (!(stream is MemoryStream || stream is MemoryBlockStream)) dispose = true; chained.Add (stream); } foreach (var tag in tags) ctx.Sections.Remove (tag); } finally { ctx.Dispose (); } var entity = ParseEntity (chained, dispose, cancellationToken); if (partSpecifier.Length == 0) { for (int i = entity.Headers.Count; i > 0; i--) { var header = entity.Headers[i - 1]; if (!header.Field.StartsWith ("Content-", StringComparison.OrdinalIgnoreCase)) entity.Headers.RemoveAt (i - 1); } } return entity; }
/// <summary> /// Get the quota information for the folder. /// </summary> /// <remarks> /// <para>Gets the quota information for the folder.</para> /// <para>To determine if a quotas are supported, check the /// <see cref="ImapClient.SupportsQuotas"/> property.</para> /// </remarks> /// <returns>The folder quota.</returns> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="ServiceNotAuthenticatedException"> /// The <see cref="ImapClient"/> is not authenticated. /// </exception> /// <exception cref="System.NotSupportedException"> /// The IMAP server does not support the QUOTA extension. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// The server's response contained unexpected tokens. /// </exception> /// <exception cref="ImapCommandException"> /// The server replied with a NO or BAD response. /// </exception> public override FolderQuota GetQuota (CancellationToken cancellationToken = default (CancellationToken)) { CheckState (false, false); if ((Engine.Capabilities & ImapCapabilities.Quota) == 0) throw new NotSupportedException ("The IMAP server does not support the QUOTA extension."); var ic = new ImapCommand (Engine, cancellationToken, null, "GETQUOTAROOT %F\r\n", this); ic.RegisterUntaggedHandler ("QUOTAROOT", UntaggedQuotaRoot); ic.RegisterUntaggedHandler ("QUOTA", UntaggedQuota); Engine.QueueCommand (ic); Engine.Wait (ic); ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create ("GETQUOTAROOT", ic); if (ic.UserData == null) return new FolderQuota (null); return (FolderQuota) ic.UserData; }
/// <summary> /// Set the quota limits for the folder. /// </summary> /// <remarks> /// <para>Sets the quota limits for the folder.</para> /// <para>To determine if a quotas are supported, check the /// <see cref="ImapClient.SupportsQuotas"/> property.</para> /// </remarks> /// <returns>The folder quota.</returns> /// <param name="messageLimit">If not <c>null</c>, sets the maximum number of messages to allow.</param> /// <param name="storageLimit">If not <c>null</c>, sets the maximum storage size (in kilobytes).</param> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="ServiceNotAuthenticatedException"> /// The <see cref="ImapClient"/> is not authenticated. /// </exception> /// <exception cref="System.NotSupportedException"> /// The IMAP server does not support the QUOTA extension. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// The server's response contained unexpected tokens. /// </exception> /// <exception cref="ImapCommandException"> /// The server replied with a NO or BAD response. /// </exception> public override FolderQuota SetQuota (uint? messageLimit, uint? storageLimit, CancellationToken cancellationToken = default (CancellationToken)) { CheckState (false, false); if ((Engine.Capabilities & ImapCapabilities.Quota) == 0) throw new NotSupportedException ("The IMAP server does not support the QUOTA extension."); var command = new StringBuilder ("SETQUOTA %F ("); if (messageLimit.HasValue) command.AppendFormat ("MESSAGE {0}", messageLimit.Value); if (storageLimit.HasValue) command.AppendFormat ("STORAGE {0}", storageLimit.Value); command.Append (")\r\n"); var ic = new ImapCommand (Engine, cancellationToken, null, command.ToString (), this); ic.RegisterUntaggedHandler ("QUOTA", UntaggedQuota); Engine.QueueCommand (ic); Engine.Wait (ic); ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create ("SETQUOTA", ic); if (ic.UserData == null) return new FolderQuota (null); return (FolderQuota) ic.UserData; }
/// <summary> /// Gets the specified subfolder. /// </summary> /// <remarks> /// Gets the specified subfolder. /// </remarks> /// <returns>The subfolder.</returns> /// <param name="name">The name of the subfolder.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="name"/> is <c>null</c>. /// </exception> /// <exception cref="System.ArgumentException"> /// <paramref name="name"/> is either an empty string or contains the <see cref="MailFolder.DirectorySeparator"/>. /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="ServiceNotAuthenticatedException"> /// The <see cref="ImapClient"/> is not authenticated. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="FolderNotFoundException"> /// The requested folder could not be found. /// </exception> /// <exception cref="ImapProtocolException"> /// The server's response contained unexpected tokens. /// </exception> /// <exception cref="ImapCommandException"> /// The server replied with a NO or BAD response. /// </exception> public override IMailFolder GetSubfolder (string name, CancellationToken cancellationToken = default (CancellationToken)) { if (name == null) throw new ArgumentNullException ("name"); if (!Engine.IsValidMailboxName (name, DirectorySeparator)) throw new ArgumentException ("The name of the subfolder is invalid.", "name"); CheckState (false, false); var fullName = FullName.Length > 0 ? FullName + DirectorySeparator + name : name; var encodedName = Engine.EncodeMailboxName (fullName); var list = new List<ImapFolder> (); ImapFolder subfolder; if (Engine.GetCachedFolder (encodedName, out subfolder)) return subfolder; var ic = new ImapCommand (Engine, cancellationToken, null, "LIST \"\" %S\r\n", encodedName); ic.RegisterUntaggedHandler ("LIST", ImapUtils.ParseFolderList); ic.UserData = list; Engine.QueueCommand (ic); Engine.Wait (ic); ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create ("LIST", ic); if (list.Count == 0) throw new FolderNotFoundException (fullName); return list[0]; }
/// <summary> /// Get the access rights for the current authenticated user. /// </summary> /// <remarks> /// Gets the access rights for the current authenticated user. /// </remarks> /// <returns>The access rights.</returns> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="ServiceNotAuthenticatedException"> /// The <see cref="ImapClient"/> is not authenticated. /// </exception> /// <exception cref="System.NotSupportedException"> /// The IMAP server does not support the ACL extension. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// The server's response contained unexpected tokens. /// </exception> /// <exception cref="ImapCommandException"> /// The command failed. /// </exception> public override AccessRights GetMyAccessRights (CancellationToken cancellationToken = default (CancellationToken)) { if ((Engine.Capabilities & ImapCapabilities.Acl) == 0) throw new NotSupportedException ("The IMAP server does not support the ACL extension."); CheckState (false, false); var ic = new ImapCommand (Engine, cancellationToken, null, "MYRIGHTS %F\r\n", this); ic.RegisterUntaggedHandler ("MYRIGHTS", UntaggedMyRights); ic.UserData = new AccessRights (); Engine.QueueCommand (ic); Engine.Wait (ic); ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create ("MYRIGHTS", ic); return (AccessRights) ic.UserData; }
/// <summary> /// Gets the subfolders. /// </summary> /// <remarks> /// Gets the subfolders. /// </remarks> /// <returns>The subfolders.</returns> /// <param name="subscribedOnly">If set to <c>true</c>, only subscribed folders will be listed.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="ServiceNotAuthenticatedException"> /// The <see cref="ImapClient"/> is not authenticated. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// The server's response contained unexpected tokens. /// </exception> /// <exception cref="ImapCommandException"> /// The server replied with a NO or BAD response. /// </exception> public override IEnumerable<IMailFolder> GetSubfolders (bool subscribedOnly = false, CancellationToken cancellationToken = default (CancellationToken)) { CheckState (false, false); var pattern = EncodedName.Length > 0 ? EncodedName + DirectorySeparator : string.Empty; var command = subscribedOnly ? "LSUB" : "LIST"; var children = new List<IMailFolder> (); var list = new List<ImapFolder> (); var ic = new ImapCommand (Engine, cancellationToken, null, command + " \"\" %S\r\n", pattern + "%"); ic.RegisterUntaggedHandler (command, ImapUtils.ParseFolderList); ic.UserData = list; Engine.QueueCommand (ic); Engine.Wait (ic); // Note: Some broken IMAP servers (*cough* SmarterMail 13.0 *cough*) return folders // that are not children of the folder we requested, so we need to filter those // folders out of the list we'll be returning to our caller. // // See https://github.com/jstedfast/MailKit/issues/149 for more details. var prefix = FullName.Length > 0 ? FullName + DirectorySeparator : string.Empty; prefix = ImapUtils.CanonicalizeMailboxName (prefix, DirectorySeparator); foreach (var folder in list) { var canonicalFullName = ImapUtils.CanonicalizeMailboxName (folder.FullName, folder.DirectorySeparator); var canonicalName = ImapUtils.IsInbox (folder.Name) ? "INBOX" : folder.Name; if (canonicalFullName != prefix + canonicalName) continue; folder.ParentFolder = this; children.Add (folder); } ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create (subscribedOnly ? "LSUB" : "LIST", ic); return children; }
/// <summary> /// Identify the client implementation to the server and obtain the server implementation details. /// </summary> /// <remarks> /// <para>Passes along the client implementation details to the server while also obtaining implementation /// details from the server.</para> /// <para>If the <paramref name="clientImplementation"/> is null or no properties have been set, no /// identifying information will be sent to the server.</para> /// <para>Security Implications</para> /// <para>This command has the danger of violating the privacy of users if misused. Clients should /// notify users that they send the ID command.</para> /// <para>It is highly desirable that implementations provide a method of disabling ID support, perhaps by /// not calling this method at all, or by passing <c>null</c> as the <paramref name="clientImplementation"/> /// argument.</para> /// <para>Implementors must exercise extreme care in adding properties to the <paramref name="clientImplementation"/>. /// Some properties, such as a processor ID number, Ethernet address, or other unique (or mostly unique) identifier /// would allow tracking of users in ways that violate user privacy expectations and may also make it easier for /// attackers to exploit security holes in the client.</para> /// </remarks> /// <example> /// <code language="c#" source="Examples\ImapExamples.cs" region="Capabilities"/> /// </example> /// <returns>The implementation details of the server.</returns> /// <param name="clientImplementation">The client implementation.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="System.NotSupportedException"> /// The IMAP server does not support the ID extension. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapCommandException"> /// The server replied to the ID command with a NO or BAD response. /// </exception> /// <exception cref="ImapProtocolException"> /// An IMAP protocol error occurred. /// </exception> public ImapImplementation Identify (ImapImplementation clientImplementation, CancellationToken cancellationToken = default (CancellationToken)) { CheckDisposed (); CheckConnected (); if ((engine.Capabilities & ImapCapabilities.Id) == 0) throw new NotSupportedException ("The IMAP server does not support the ID extension."); var command = new StringBuilder ("ID "); var args = new List<object> (); if (clientImplementation != null && clientImplementation.Properties.Count > 0) { command.Append ('('); foreach (var property in clientImplementation.Properties) { command.Append ("%Q "); args.Add (property.Key); if (property.Value != null) { command.Append ("%Q "); args.Add (property.Value); } else { command.Append ("NIL "); } } command[command.Length - 1] = ')'; command.Append ("\r\n"); } else { command.Append ("NIL\r\n"); } var ic = new ImapCommand (engine, cancellationToken, null, command.ToString (), args.ToArray ()); ic.RegisterUntaggedHandler ("ID", ImapUtils.ParseImplementation); engine.QueueCommand (ic); engine.Wait (ic); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create ("ID", ic); return (ImapImplementation) ic.UserData; }
/// <summary> /// Queries the special folders. /// </summary> /// <returns>The command result.</returns> /// <param name="cancellationToken">The cancellation token.</param> public void QuerySpecialFolders(CancellationToken cancellationToken) { if (stream == null) throw new InvalidOperationException (); ImapFolder folder; if (!FolderCache.TryGetValue ("INBOX", out folder)) { var list = new List<ImapFolder> (); var ic = new ImapCommand (this, cancellationToken, null, "LIST \"\" \"INBOX\"\r\n"); ic.RegisterUntaggedHandler ("LIST", ImapUtils.ParseFolderList); ic.UserData = list; QueueCommand (ic); Wait (ic); Inbox = list.Count > 0 ? list[0] : null; } if ((Capabilities & ImapCapabilities.SpecialUse) != 0) { var list = new List<ImapFolder> (); var ic = new ImapCommand (this, cancellationToken, null, "LIST (SPECIAL-USE) \"\" \"*\"\r\n"); ic.RegisterUntaggedHandler ("LIST", ImapUtils.ParseFolderList); ic.UserData = list; QueueCommand (ic); Wait (ic); LookupParentFolders (list, cancellationToken); for (int i = 0; i < list.Count; i++) { folder = list[i]; if ((folder.Attributes & FolderAttributes.All) != 0) All = folder; if ((folder.Attributes & FolderAttributes.Archive) != 0) Archive = folder; if ((folder.Attributes & FolderAttributes.Drafts) != 0) Drafts = folder; if ((folder.Attributes & FolderAttributes.Flagged) != 0) Flagged = folder; if ((folder.Attributes & FolderAttributes.Junk) != 0) Junk = folder; if ((folder.Attributes & FolderAttributes.Sent) != 0) Sent = folder; if ((folder.Attributes & FolderAttributes.Trash) != 0) Trash = folder; } } else if ((Capabilities & ImapCapabilities.XList) != 0) { var list = new List<ImapFolder> (); var ic = new ImapCommand (this, cancellationToken, null, "XLIST \"\" \"*\"\r\n"); ic.RegisterUntaggedHandler ("XLIST", ImapUtils.ParseFolderList); ic.UserData = list; QueueCommand (ic); Wait (ic); LookupParentFolders (list, cancellationToken); for (int i = 0; i < list.Count; i++) { folder = list[i]; if ((folder.Attributes & FolderAttributes.All) != 0) All = folder; if ((folder.Attributes & FolderAttributes.Archive) != 0) Archive = folder; if ((folder.Attributes & FolderAttributes.Drafts) != 0) Drafts = folder; if ((folder.Attributes & FolderAttributes.Flagged) != 0) Flagged = folder; if ((folder.Attributes & FolderAttributes.Junk) != 0) Junk = folder; if ((folder.Attributes & FolderAttributes.Sent) != 0) Sent = folder; if ((folder.Attributes & FolderAttributes.Trash) != 0) Trash = folder; } } }
/// <summary> /// Fetches the message summaries for the messages between the two indexes, inclusive. /// </summary> /// <remarks> /// <para>Fetches the message summaries for the messages between the two /// indexes, inclusive.</para> /// <para>It should be noted that if another client has modified any message /// in the folder, the IMAP server may choose to return information that was /// not explicitly requested. It is therefore important to be prepared to /// handle both additional fields on a <see cref="IMessageSummary"/> for /// messages that were requested as well as summaries for messages that were /// not requested at all.</para> /// </remarks> /// <returns>An enumeration of summaries for the requested messages.</returns> /// <param name="min">The minimum index.</param> /// <param name="max">The maximum index, or <c>-1</c> to specify no upper bound.</param> /// <param name="items">The message summary items to fetch.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ArgumentOutOfRangeException"> /// <para><paramref name="min"/> is out of range.</para> /// <para>-or-</para> /// <para><paramref name="max"/> is out of range.</para> /// <para>-or-</para> /// <para><paramref name="items"/> is empty.</para> /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="ServiceNotAuthenticatedException"> /// The <see cref="ImapClient"/> is not authenticated. /// </exception> /// <exception cref="FolderNotOpenException"> /// The <see cref="ImapFolder"/> is not currently open. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// The server's response contained unexpected tokens. /// </exception> /// <exception cref="ImapCommandException"> /// The server replied with a NO or BAD response. /// </exception> public override IList<IMessageSummary> Fetch (int min, int max, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)) { if (min < 0 || min > Count) throw new ArgumentOutOfRangeException ("min"); if (max != -1 && max < min) throw new ArgumentOutOfRangeException ("max"); if (items == MessageSummaryItems.None) throw new ArgumentOutOfRangeException ("items"); CheckState (true, false); if (min == Count) return new IMessageSummary[0]; var query = FormatSummaryItems (ref items, null); var command = string.Format ("FETCH {0} {1}\r\n", GetFetchRange (min, max), query); var ic = new ImapCommand (Engine, cancellationToken, this, command); var ctx = new FetchSummaryContext (items); ic.RegisterUntaggedHandler ("FETCH", FetchSummaryItems); ic.UserData = ctx; Engine.QueueCommand (ic); Engine.Wait (ic); ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create ("FETCH", ic); return AsReadOnly (ctx.Results.Values); }
/// <summary> /// Gets the specified metadata. /// </summary> /// <remarks> /// Gets the specified metadata. /// </remarks> /// <returns>The requested metadata value.</returns> /// <param name="tag">The metadata tag.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="ServiceNotAuthenticatedException"> /// The <see cref="ImapClient"/> is not authenticated. /// </exception> /// <exception cref="System.NotSupportedException"> /// The IMAP server does not support the METADATA extension. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// The server's response contained unexpected tokens. /// </exception> /// <exception cref="ImapCommandException"> /// The server replied with a NO or BAD response. /// </exception> public override string GetMetadata (MetadataTag tag, CancellationToken cancellationToken = default (CancellationToken)) { CheckState (false, false); if ((Engine.Capabilities & ImapCapabilities.Metadata) == 0) throw new NotSupportedException ("The IMAP server does not support the METADATA extension."); var ic = new ImapCommand (Engine, cancellationToken, null, "GETMETADATA %F (%S)\r\n", this, tag.Id); ic.RegisterUntaggedHandler ("METADATA", UntaggedMetadata); var metadata = new MetadataCollection (); ic.UserData = metadata; Engine.QueueCommand (ic); Engine.Wait (ic); ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create ("GETMETADATA", ic); for (int i = 0; i < metadata.Count; i++) { if (metadata[i].Tag.Id == tag.Id) return metadata[i].Value; } return null; }
/// <summary> /// Fetches the message summaries for the messages between the two indexes (inclusive) /// that have a higher mod-sequence value than the one specified. /// </summary> /// <remarks> /// <para>Fetches the message summaries for the messages between the two /// indexes (inclusive) that have a higher mod-sequence value than the one /// specified.</para> /// <para>It should be noted that if another client has modified any message /// in the folder, the IMAP server may choose to return information that was /// not explicitly requested. It is therefore important to be prepared to /// handle both additional fields on a <see cref="IMessageSummary"/> for /// messages that were requested as well as summaries for messages that were /// not requested at all.</para> /// </remarks> /// <returns>An enumeration of summaries for the requested messages.</returns> /// <param name="min">The minimum index.</param> /// <param name="max">The maximum index, or <c>-1</c> to specify no upper bound.</param> /// <param name="modseq">The mod-sequence value.</param> /// <param name="items">The message summary items to fetch.</param> /// <param name="fields">The desired header fields.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ArgumentOutOfRangeException"> /// <para><paramref name="min"/> is out of range.</para> /// <para>-or-</para> /// <para><paramref name="max"/> is out of range.</para> /// </exception> /// <exception cref="System.ArgumentNullException"> /// <paramref name="fields"/> is <c>null</c>. /// </exception> /// <exception cref="System.ArgumentException"> /// <paramref name="fields"/> is empty. /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="ServiceNotAuthenticatedException"> /// The <see cref="ImapClient"/> is not authenticated. /// </exception> /// <exception cref="FolderNotOpenException"> /// The <see cref="ImapFolder"/> is not currently open. /// </exception> /// <exception cref="System.NotSupportedException"> /// The <see cref="ImapFolder"/> does not support mod-sequences. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// The server's response contained unexpected tokens. /// </exception> /// <exception cref="ImapCommandException"> /// The server replied with a NO or BAD response. /// </exception> public override IList<IMessageSummary> Fetch (int min, int max, ulong modseq, MessageSummaryItems items, HashSet<string> fields, CancellationToken cancellationToken = default (CancellationToken)) { if (min < 0 || min >= Count) throw new ArgumentOutOfRangeException ("min"); if (max != -1 && max < min) throw new ArgumentOutOfRangeException ("max"); if (fields == null) throw new ArgumentNullException ("fields"); if (fields.Count == 0) throw new ArgumentException ("The set of header fields cannot be empty.", "fields"); if (!SupportsModSeq) throw new NotSupportedException ("The ImapFolder does not support mod-sequences."); CheckState (true, false); var query = FormatSummaryItems (ref items, fields); var command = string.Format ("FETCH {0} {1} (CHANGEDSINCE {2})\r\n", GetFetchRange (min, max), query, modseq); var ic = new ImapCommand (Engine, cancellationToken, this, command); var ctx = new FetchSummaryContext (items); ic.RegisterUntaggedHandler ("FETCH", FetchSummaryItems); ic.UserData = ctx; Engine.QueueCommand (ic); Engine.Wait (ic); ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create ("FETCH", ic); return AsReadOnly (ctx.Results.Values); }
/// <summary> /// Gets the specified metadata. /// </summary> /// <remarks> /// Gets the specified metadata. /// </remarks> /// <returns>The requested metadata.</returns> /// <param name="tags">The metadata tags.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="tags"/> is <c>null</c>. /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="ServiceNotAuthenticatedException"> /// The <see cref="ImapClient"/> is not authenticated. /// </exception> /// <exception cref="System.NotSupportedException"> /// The IMAP server does not support the METADATA extension. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// The server's response contained unexpected tokens. /// </exception> /// <exception cref="ImapCommandException"> /// The server replied with a NO or BAD response. /// </exception> public override MetadataCollection GetMetadata (IEnumerable<MetadataTag> tags, CancellationToken cancellationToken = default (CancellationToken)) { if (tags == null) throw new ArgumentNullException ("tags"); CheckState (false, false); if ((Engine.Capabilities & ImapCapabilities.Metadata) == 0) throw new NotSupportedException ("The IMAP server does not support the METADATA extension."); var command = new StringBuilder ("GETMETADATA %F ("); var args = new List<object> (); args.Add (this); foreach (var tag in tags) { if (args.Count > 1) command.Append (' '); command.Append ("%S"); args.Add (tag.Id); } command.Append (")\r\n"); var ic = new ImapCommand (Engine, cancellationToken, null, command.ToString (), args.ToArray ()); ic.RegisterUntaggedHandler ("METADATA", UntaggedMetadata); ic.UserData = new MetadataCollection (); Engine.QueueCommand (ic); Engine.Wait (ic); ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create ("GETMETADATA", ic); return (MetadataCollection) ic.UserData; }
/// <summary> /// Gets the specified message. /// </summary> /// <remarks> /// Gets the specified message. /// </remarks> /// <returns>The message.</returns> /// <param name="index">The index of the message.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="progress">The progress reporting mechanism.</param> /// <exception cref="System.ArgumentOutOfRangeException"> /// <paramref name="index"/> is out of range. /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="ServiceNotAuthenticatedException"> /// The <see cref="ImapClient"/> is not authenticated. /// </exception> /// <exception cref="FolderNotOpenException"> /// The <see cref="ImapFolder"/> is not currently open. /// </exception> /// <exception cref="MessageNotFoundException"> /// The IMAP server did not return the requested message. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// The server's response contained unexpected tokens. /// </exception> /// <exception cref="ImapCommandException"> /// The server replied with a NO or BAD response. /// </exception> public override MimeMessage GetMessage (int index, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException ("index"); CheckState (true, false); var ic = new ImapCommand (Engine, cancellationToken, this, "FETCH %d (BODY.PEEK[])\r\n", index + 1); var ctx = new FetchStreamContext (progress); Stream stream; ic.RegisterUntaggedHandler ("FETCH", FetchStream); ic.UserData = ctx; Engine.QueueCommand (ic); try { Engine.Wait (ic); ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create ("FETCH", ic); if (!ctx.Sections.TryGetValue (string.Empty, out stream)) throw new MessageNotFoundException ("The IMAP server did not return the requested message."); ctx.Sections.Remove (string.Empty); } finally { ctx.Dispose (); } return ParseMessage (stream, cancellationToken); }
/// <summary> /// Opens the folder using the requested folder access. /// </summary> /// <remarks> /// <para>This variant of the <see cref="Open(FolderAccess,System.Threading.CancellationToken)"/> /// method is meant for quick resynchronization of the folder. Before calling this method, /// the <see cref="ImapClient.EnableQuickResync(CancellationToken)"/> method MUST be called.</para> /// <para>You should also make sure to add listeners to the <see cref="MailFolder.MessagesVanished"/> and /// <see cref="MailFolder.MessageFlagsChanged"/> events to get notifications of changes since /// the last time the folder was opened.</para> /// </remarks> /// <returns>The <see cref="FolderAccess"/> state of the folder.</returns> /// <param name="access">The requested folder access.</param> /// <param name="uidValidity">The last known <see cref="MailFolder.UidValidity"/> value.</param> /// <param name="highestModSeq">The last known <see cref="MailFolder.HighestModSeq"/> value.</param> /// <param name="uids">The last known list of unique message identifiers.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ArgumentOutOfRangeException"> /// <paramref name="access"/> is not a valid value. /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="ServiceNotAuthenticatedException"> /// The <see cref="ImapClient"/> is not authenticated. /// </exception> /// <exception cref="FolderNotFoundException"> /// The <see cref="ImapFolder"/> does not exist. /// </exception> /// <exception cref="System.InvalidOperationException"> /// The QRESYNC feature has not been enabled. /// </exception> /// <exception cref="System.NotSupportedException"> /// The IMAP server does not support the QRESYNC extension. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// The server's response contained unexpected tokens. /// </exception> /// <exception cref="ImapCommandException"> /// The server replied with a NO or BAD response. /// </exception> public override FolderAccess Open (FolderAccess access, uint uidValidity, ulong highestModSeq, IList<UniqueId> uids, CancellationToken cancellationToken = default (CancellationToken)) { var set = ImapUtils.FormatUidSet (uids); if (access != FolderAccess.ReadOnly && access != FolderAccess.ReadWrite) throw new ArgumentOutOfRangeException ("access"); CheckState (false, false); if (IsOpen && Access == access) return access; if ((Engine.Capabilities & ImapCapabilities.QuickResync) == 0) throw new NotSupportedException ("The IMAP server does not support the QRESYNC extension."); if (!Engine.QResyncEnabled) throw new InvalidOperationException ("The QRESYNC extension has not been enabled."); var qresync = string.Format ("(QRESYNC ({0} {1}", uidValidity, highestModSeq); if (uids.Count > 0) qresync += " " + set; qresync += "))"; var command = string.Format ("{0} %F {1}\r\n", SelectOrExamine (access), qresync); var ic = new ImapCommand (Engine, cancellationToken, this, command, this); ic.RegisterUntaggedHandler ("FETCH", QResyncFetch); if (access == FolderAccess.ReadWrite) { // Note: if the server does not respond with a PERMANENTFLAGS response, // then we need to assume all flags are permanent. PermanentFlags = SettableFlags | MessageFlags.UserDefined; } else { PermanentFlags = MessageFlags.None; } try { Engine.QueueCommand (ic); Engine.Wait (ic); ProcessResponseCodes (ic, this); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create (access == FolderAccess.ReadOnly ? "EXAMINE" : "SELECT", ic); } catch { PermanentFlags = MessageFlags.None; throw; } if (Engine.Selected != null && Engine.Selected != this) { var folder = Engine.Selected; folder.PermanentFlags = MessageFlags.None; folder.AcceptedFlags = MessageFlags.None; folder.Access = FolderAccess.None; folder.OnClosed (); } Engine.State = ImapEngineState.Selected; Engine.Selected = this; OnOpened (); return Access; }
/// <summary> /// Gets a substream of the specified message. /// </summary> /// <remarks> /// Fetches a substream of the message. If the starting offset is beyond /// the end of the message, an empty stream is returned. If the number of /// bytes desired extends beyond the end of the message, a truncated stream /// will be returned. /// </remarks> /// <returns>The stream.</returns> /// <param name="uid">The UID of the message.</param> /// <param name="offset">The starting offset of the first desired byte.</param> /// <param name="count">The number of bytes desired.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="progress">The progress reporting mechanism.</param> /// <exception cref="System.ArgumentException"> /// <paramref name="uid"/> is invalid. /// </exception> /// <exception cref="System.ArgumentOutOfRangeException"> /// <para><paramref name="offset"/> is negative.</para> /// <para>-or-</para> /// <para><paramref name="count"/> is negative.</para> /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="ServiceNotAuthenticatedException"> /// The <see cref="ImapClient"/> is not authenticated. /// </exception> /// <exception cref="FolderNotOpenException"> /// The <see cref="ImapFolder"/> is not currently open. /// </exception> /// <exception cref="MessageNotFoundException"> /// The IMAP server did not return the requested message stream. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// The server's response contained unexpected tokens. /// </exception> /// <exception cref="ImapCommandException"> /// The server replied with a NO or BAD response. /// </exception> public override Stream GetStream (UniqueId uid, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) { if (uid.Id == 0) throw new ArgumentException ("The uid is invalid.", "uid"); if (offset < 0) throw new ArgumentOutOfRangeException ("offset"); if (count < 0) throw new ArgumentOutOfRangeException ("count"); CheckState (true, false); if (count == 0) return new MemoryStream (); var ic = new ImapCommand (Engine, cancellationToken, this, "UID FETCH %u (BODY.PEEK[]<%d.%d>)\r\n", uid.Id, offset, count); var ctx = new FetchStreamContext (progress); Stream stream; ic.RegisterUntaggedHandler ("FETCH", FetchStream); ic.UserData = ctx; Engine.QueueCommand (ic); try { Engine.Wait (ic); ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create ("FETCH", ic); if (!ctx.Sections.TryGetValue (string.Empty, out stream)) throw new MessageNotFoundException ("The IMAP server did not return the requested stream."); ctx.Sections.Remove (string.Empty); } finally { ctx.Dispose (); } return stream; }
/// <summary> /// Fetches the message summaries for the specified message UIDs. /// </summary> /// <remarks> /// <para>Fetches the message summaries for the specified message UIDs.</para> /// <para>It should be noted that if another client has modified any message /// in the folder, the IMAP server may choose to return information that was /// not explicitly requested. It is therefore important to be prepared to /// handle both additional fields on a <see cref="IMessageSummary"/> for /// messages that were requested as well as summaries for messages that were /// not requested at all.</para> /// </remarks> /// <returns>An enumeration of summaries for the requested messages.</returns> /// <param name="uids">The UIDs.</param> /// <param name="items">The message summary items to fetch.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="uids"/> is <c>null</c>. /// </exception> /// <exception cref="System.ArgumentOutOfRangeException"> /// <paramref name="items"/> is empty. /// </exception> /// <exception cref="System.ArgumentException"> /// One or more of the <paramref name="uids"/> is invalid. /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="FolderNotOpenException"> /// The <see cref="ImapFolder"/> is not currently open. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="ServiceNotAuthenticatedException"> /// The <see cref="ImapClient"/> is not authenticated. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// The server's response contained unexpected tokens. /// </exception> /// <exception cref="ImapCommandException"> /// The server replied with a NO or BAD response. /// </exception> public override IList<IMessageSummary> Fetch (IList<UniqueId> uids, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)) { var set = ImapUtils.FormatUidSet (uids); if (items == MessageSummaryItems.None) throw new ArgumentOutOfRangeException ("items"); CheckState (true, false); if (uids.Count == 0) return new IMessageSummary[0]; var query = FormatSummaryItems (ref items, null); var command = string.Format ("UID FETCH {0} {1}\r\n", set, query); var ic = new ImapCommand (Engine, cancellationToken, this, command); var ctx = new FetchSummaryContext (items); ic.RegisterUntaggedHandler ("FETCH", FetchSummaryItems); ic.UserData = ctx; Engine.QueueCommand (ic); Engine.Wait (ic); ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create ("FETCH", ic); return AsReadOnly (ctx.Results.Values); }
/// <summary> /// Creates a new subfolder with the given name. /// </summary> /// <remarks> /// Creates a new subfolder with the given name. /// </remarks> /// <returns>The created folder.</returns> /// <param name="name">The name of the folder to create.</param> /// <param name="specialUses">A list of special uses for the folder being created.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="name"/> is <c>null</c>. /// </exception> /// <exception cref="System.ArgumentException"> /// <paramref name="name"/> is empty. /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="ServiceNotAuthenticatedException"> /// The <see cref="ImapClient"/> is not authenticated. /// </exception> /// <exception cref="System.InvalidOperationException"> /// The <see cref="MailFolder.DirectorySeparator"/> is nil, and thus child folders cannot be created. /// </exception> /// <exception cref="System.NotSupportedException"> /// The IMAP server does not support the CREATE-SPECIAL-USE extension. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// The server's response contained unexpected tokens. /// </exception> /// <exception cref="ImapCommandException"> /// The server replied with a NO or BAD response. /// </exception> public override IMailFolder Create (string name, IEnumerable<SpecialFolder> specialUses, CancellationToken cancellationToken = default (CancellationToken)) { if (name == null) throw new ArgumentNullException ("name"); if (!Engine.IsValidMailboxName (name, DirectorySeparator)) throw new ArgumentException ("The name is not a legal folder name.", "name"); CheckState (false, false); if (!string.IsNullOrEmpty (FullName) && DirectorySeparator == '\0') throw new InvalidOperationException ("Cannot create child folders."); if ((Engine.Capabilities & ImapCapabilities.CreateSpecialUse) == 0) throw new NotSupportedException ("The IMAP server does not support the CREATE-SPECIAL-USE extension."); var uses = new StringBuilder (); foreach (var use in specialUses) { if (uses.Length > 0) uses.Append (' '); switch (use) { case SpecialFolder.All: uses.Append ("\\All"); break; case SpecialFolder.Archive: uses.Append ("\\Archive"); break; case SpecialFolder.Drafts: uses.Append ("\\Drafts"); break; case SpecialFolder.Flagged: uses.Append ("\\Flagged"); break; case SpecialFolder.Junk: uses.Append ("\\Junk"); break; case SpecialFolder.Sent: uses.Append ("\\Sent"); break; case SpecialFolder.Trash: uses.Append ("\\Trash"); break; default: if (uses.Length > 0) uses.Length--; break; } } var fullName = !string.IsNullOrEmpty (FullName) ? FullName + DirectorySeparator + name : name; var command = string.Format ("CREATE %s (USE ({0}))\r\n", uses); var encodedName = Engine.EncodeMailboxName (fullName); var list = new List<ImapFolder> (); var createName = encodedName; ImapFolder folder; var ic = Engine.QueueCommand (cancellationToken, null, command, createName); Engine.Wait (ic); ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) { var useAttr = ic.RespCodes.FirstOrDefault (rc => rc.Type == ImapResponseCodeType.UseAttr); if (useAttr != null) throw new ImapCommandException (ic.Response, useAttr.Message); throw ImapCommandException.Create ("CREATE", ic); } ic = new ImapCommand (Engine, cancellationToken, null, "LIST \"\" %S\r\n", encodedName); ic.RegisterUntaggedHandler ("LIST", ImapUtils.ParseFolderList); ic.UserData = list; Engine.QueueCommand (ic); Engine.Wait (ic); ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create ("LIST", ic); if ((folder = list.FirstOrDefault ()) != null) folder.ParentFolder = this; Engine.AssignSpecialFolders (new [] { folder }); return folder; }
/// <summary> /// Fetches the message summaries for the specified message UIDs that have a /// higher mod-sequence value than the one specified. /// </summary> /// <remarks> /// <para>Fetches the message summaries for the specified message UIDs that /// have a higher mod-sequence value than the one specified.</para> /// <para>If the IMAP server supports the QRESYNC extension and the application has /// enabled this feature via <see cref="ImapClient.EnableQuickResync(CancellationToken)"/>, /// then this method will emit <see cref="MailFolder.MessagesVanished"/> events for messages /// that have vanished since the specified mod-sequence value.</para> /// <para>It should be noted that if another client has modified any message /// in the folder, the IMAP server may choose to return information that was /// not explicitly requested. It is therefore important to be prepared to /// handle both additional fields on a <see cref="IMessageSummary"/> for /// messages that were requested as well as summaries for messages that were /// not requested at all.</para> /// </remarks> /// <returns>An enumeration of summaries for the requested messages.</returns> /// <param name="uids">The UIDs.</param> /// <param name="modseq">The mod-sequence value.</param> /// <param name="items">The message summary items to fetch.</param> /// <param name="fields">The desired header fields.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="uids"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="fields"/> is <c>null</c>.</para> /// </exception> /// <exception cref="System.ArgumentException"> /// <para>One or more of the <paramref name="uids"/> is invalid.</para> /// <para>-or-</para> /// <para><paramref name="fields"/> is empty.</para> /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="ServiceNotAuthenticatedException"> /// The <see cref="ImapClient"/> is not authenticated. /// </exception> /// <exception cref="FolderNotOpenException"> /// The <see cref="ImapFolder"/> is not currently open. /// </exception> /// <exception cref="System.NotSupportedException"> /// The <see cref="ImapFolder"/> does not support mod-sequences. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// The server's response contained unexpected tokens. /// </exception> /// <exception cref="ImapCommandException"> /// The server replied with a NO or BAD response. /// </exception> public override IList<IMessageSummary> Fetch (IList<UniqueId> uids, ulong modseq, MessageSummaryItems items, HashSet<string> fields, CancellationToken cancellationToken = default (CancellationToken)) { var set = ImapUtils.FormatUidSet (uids); if (fields == null) throw new ArgumentNullException ("fields"); if (fields.Count == 0) throw new ArgumentException ("The set of header fields cannot be empty.", "fields"); if (!SupportsModSeq) throw new NotSupportedException ("The ImapFolder does not support mod-sequences."); CheckState (true, false); if (uids.Count == 0) return new IMessageSummary[0]; var query = FormatSummaryItems (ref items, fields); var vanished = Engine.QResyncEnabled ? " VANISHED" : string.Empty; var command = string.Format ("UID FETCH {0} {1} (CHANGEDSINCE {2}{3})\r\n", set, query, modseq, vanished); var ic = new ImapCommand (Engine, cancellationToken, this, command); var ctx = new FetchSummaryContext (items); ic.RegisterUntaggedHandler ("FETCH", FetchSummaryItems); ic.UserData = ctx; Engine.QueueCommand (ic); Engine.Wait (ic); ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create ("FETCH", ic); return AsReadOnly (ctx.Results.Values); }
/// <summary> /// Searches the subset of UIDs in the folder for messages matching the specified query. /// </summary> /// <remarks> /// The returned array of unique identifiers can be used with methods such as /// <see cref="IMailFolder.GetMessage(UniqueId,CancellationToken,ITransferProgress)"/>. /// </remarks> /// <returns>An array of matching UIDs.</returns> /// <param name="uids">The subset of UIDs</param> /// <param name="query">The search query.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="uids"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="query"/> is <c>null</c>.</para> /// </exception> /// <exception cref="System.ArgumentException"> /// One or more of the <paramref name="uids"/> is invalid. /// </exception> /// <exception cref="System.NotSupportedException"> /// One or more search terms in the <paramref name="query"/> are not supported by the IMAP server. /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="ServiceNotAuthenticatedException"> /// The <see cref="ImapClient"/> is not authenticated. /// </exception> /// <exception cref="FolderNotOpenException"> /// The <see cref="ImapFolder"/> is not currently open. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// The server's response contained unexpected tokens. /// </exception> /// <exception cref="ImapCommandException"> /// The server replied with a NO or BAD response. /// </exception> public override IList<UniqueId> Search (IList<UniqueId> uids, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)) { var set = ImapUtils.FormatUidSet (uids); var args = new List<string> (); string charset; if (query == null) throw new ArgumentNullException ("query"); CheckState (true, false); if (uids.Count == 0) return new UniqueId[0]; var optimized = query.Optimize (new ImapSearchQueryOptimizer ()); var expr = BuildQueryExpression (optimized, args, out charset); var command = "UID SEARCH "; if ((Engine.Capabilities & ImapCapabilities.ESearch) != 0) command += "RETURN () "; if (args.Count > 0 && !Engine.UTF8Enabled) command += "CHARSET " + charset + " "; command += "UID " + set + " " + expr + "\r\n"; var ic = new ImapCommand (Engine, cancellationToken, this, command, args.ToArray ()); if ((Engine.Capabilities & ImapCapabilities.ESearch) != 0) ic.RegisterUntaggedHandler ("ESEARCH", ESearchMatches); // Note: always register the untagged SEARCH handler because some servers will brokenly // respond with "* SEARCH ..." instead of "* ESEARCH ..." even when using the extended // search syntax. ic.RegisterUntaggedHandler ("SEARCH", SearchMatches); Engine.QueueCommand (ic); Engine.Wait (ic); ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create ("SEARCH", ic); var results = (SearchResults) ic.UserData; if (results == null) return new UniqueId[0]; return results.UniqueIds; }
/// <summary> /// Fetches the message summaries for the specified message indexes. /// </summary> /// <remarks> /// <para>Fetches the message summaries for the specified message indexes.</para> /// <para>It should be noted that if another client has modified any message /// in the folder, the IMAP server may choose to return information that was /// not explicitly requested. It is therefore important to be prepared to /// handle both additional fields on a <see cref="IMessageSummary"/> for /// messages that were requested as well as summaries for messages that were /// not requested at all.</para> /// </remarks> /// <returns>An enumeration of summaries for the requested messages.</returns> /// <param name="indexes">The indexes.</param> /// <param name="items">The message summary items to fetch.</param> /// <param name="fields">The desired header fields.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="indexes"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="fields"/> is <c>null</c>.</para> /// </exception> /// <exception cref="System.ArgumentException"> /// <para>One or more of the <paramref name="indexes"/> is invalid.</para> /// <para>-or-</para> /// <para><paramref name="fields"/> is empty.</para> /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="ServiceNotAuthenticatedException"> /// The <see cref="ImapClient"/> is not authenticated. /// </exception> /// <exception cref="FolderNotOpenException"> /// The <see cref="ImapFolder"/> is not currently open. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// The server's response contained unexpected tokens. /// </exception> /// <exception cref="ImapCommandException"> /// The server replied with a NO or BAD response. /// </exception> public override IList<IMessageSummary> Fetch (IList<int> indexes, MessageSummaryItems items, HashSet<string> fields, CancellationToken cancellationToken = default (CancellationToken)) { var set = ImapUtils.FormatIndexSet (indexes); if (fields == null) throw new ArgumentNullException ("fields"); if (fields.Count == 0) throw new ArgumentException ("The set of header fields cannot be empty.", "fields"); CheckState (true, false); if (indexes.Count == 0) return new IMessageSummary[0]; var query = FormatSummaryItems (ref items, fields); var command = string.Format ("FETCH {0} {1}\r\n", set, query); var ic = new ImapCommand (Engine, cancellationToken, this, command); var ctx = new FetchSummaryContext (items); ic.RegisterUntaggedHandler ("FETCH", FetchSummaryItems); ic.UserData = ctx; Engine.QueueCommand (ic); Engine.Wait (ic); ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create ("FETCH", ic); return AsReadOnly (ctx.Results.Values); }
/// <summary> /// Searches the subset of UIDs in the folder for messages matching the specified query, /// returning them in the preferred sort order. /// </summary> /// <remarks> /// Searches the folder for messages matching the specified query and ordering, /// returning only the requested search results. /// </remarks> /// <returns>The search results.</returns> /// <param name="options">The search options.</param> /// <param name="uids">The subset of UIDs</param> /// <param name="query">The search query.</param> /// <param name="orderBy">The sort order.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="uids"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="query"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="orderBy"/> is <c>null</c>.</para> /// </exception> /// <exception cref="System.ArgumentException"> /// <para>One or more of the <paramref name="uids"/> is invalid.</para> /// <para>-or-</para> /// <para><paramref name="orderBy"/> is empty.</para> /// </exception> /// <exception cref="System.NotSupportedException"> /// <para>One or more search terms in the <paramref name="query"/> are not supported by the IMAP server.</para> /// <para>-or-</para> /// <para>The IMAP server does not support the ESORT extension.</para> /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="ServiceNotAuthenticatedException"> /// The <see cref="ImapClient"/> is not authenticated. /// </exception> /// <exception cref="FolderNotOpenException"> /// The <see cref="ImapFolder"/> is not currently open. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// The server's response contained unexpected tokens. /// </exception> /// <exception cref="ImapCommandException"> /// The server replied with a NO or BAD response. /// </exception> public override SearchResults Search (SearchOptions options, IList<UniqueId> uids, SearchQuery query, IList<OrderBy> orderBy, CancellationToken cancellationToken = default (CancellationToken)) { var set = ImapUtils.FormatUidSet (uids); var args = new List<string> (); string charset; if (query == null) throw new ArgumentNullException ("query"); if (orderBy == null) throw new ArgumentNullException ("orderBy"); if (orderBy.Count == 0) throw new ArgumentException ("No sort order provided.", "orderBy"); CheckState (true, false); if ((Engine.Capabilities & ImapCapabilities.ESort) == 0) throw new NotSupportedException ("The IMAP server does not support the ESORT extension."); if ((Engine.Capabilities & ImapCapabilities.SortDisplay) == 0) { for (int i = 0; i < orderBy.Count; i++) { if (orderBy[i].Type == OrderByType.DisplayFrom || orderBy[i].Type == OrderByType.DisplayTo) throw new NotSupportedException ("The IMAP server does not support the SORT=DISPLAY extension."); } } if (uids.Count == 0) return new SearchResults (); var optimized = query.Optimize (new ImapSearchQueryOptimizer ()); var expr = BuildQueryExpression (optimized, args, out charset); var order = BuildSortOrder (orderBy); var command = "UID SORT RETURN ("; if ((options & SearchOptions.Count) != 0) command += "COUNT "; if ((options & SearchOptions.Min) != 0) command += "MIN "; if ((options & SearchOptions.Max) != 0) command += "MAX "; command = command.TrimEnd (); command += ") "; command += order + " " + charset + " UID " + set + " " + expr + "\r\n"; var ic = new ImapCommand (Engine, cancellationToken, this, command, args.ToArray ()); ic.RegisterUntaggedHandler ("ESEARCH", ESearchMatches); ic.UserData = new SearchResults (); Engine.QueueCommand (ic); Engine.Wait (ic); ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create ("SORT", ic); return (SearchResults) ic.UserData; }
/// <summary> /// Fetches the message summaries for the specified message indexes that have a /// higher mod-sequence value than the one specified. /// </summary> /// <remarks> /// <para>Fetches the message summaries for the specified message indexes that /// have a higher mod-sequence value than the one specified.</para> /// <para>It should be noted that if another client has modified any message /// in the folder, the IMAP server may choose to return information that was /// not explicitly requested. It is therefore important to be prepared to /// handle both additional fields on a <see cref="IMessageSummary"/> for /// messages that were requested as well as summaries for messages that were /// not requested at all.</para> /// </remarks> /// <returns>An enumeration of summaries for the requested messages.</returns> /// <param name="indexes">The indexes.</param> /// <param name="modseq">The mod-sequence value.</param> /// <param name="items">The message summary items to fetch.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="indexes"/> is <c>null</c>. /// </exception> /// <exception cref="System.ArgumentOutOfRangeException"> /// <paramref name="items"/> is empty. /// </exception> /// <exception cref="System.ArgumentException"> /// One or more of the <paramref name="indexes"/> is invalid. /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="ServiceNotAuthenticatedException"> /// The <see cref="ImapClient"/> is not authenticated. /// </exception> /// <exception cref="FolderNotOpenException"> /// The <see cref="ImapFolder"/> is not currently open. /// </exception> /// <exception cref="System.NotSupportedException"> /// The <see cref="ImapFolder"/> does not support mod-sequences. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// The server's response contained unexpected tokens. /// </exception> /// <exception cref="ImapCommandException"> /// The server replied with a NO or BAD response. /// </exception> public override IList<IMessageSummary> Fetch (IList<int> indexes, ulong modseq, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)) { var set = ImapUtils.FormatIndexSet (indexes); if (items == MessageSummaryItems.None) throw new ArgumentOutOfRangeException ("items"); if (!SupportsModSeq) throw new NotSupportedException ("The ImapFolder does not support mod-sequences."); CheckState (true, false); if (indexes.Count == 0) return new IMessageSummary[0]; var query = FormatSummaryItems (ref items, null); var command = string.Format ("FETCH {0} {1} (CHANGEDSINCE {2})\r\n", set, query, modseq); var ic = new ImapCommand (Engine, cancellationToken, this, command); var ctx = new FetchSummaryContext (items); ic.RegisterUntaggedHandler ("FETCH", FetchSummaryItems); ic.UserData = ctx; Engine.QueueCommand (ic); Engine.Wait (ic); ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create ("FETCH", ic); return AsReadOnly (ctx.Results.Values); }
/// <summary> /// Gets the subfolders. /// </summary> /// <remarks> /// Gets the subfolders. /// </remarks> /// <returns>The subfolders.</returns> /// <param name="items">The status items to pre-populate.</param> /// <param name="subscribedOnly">If set to <c>true</c>, only subscribed folders will be listed.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="ServiceNotConnectedException"> /// The <see cref="ImapClient"/> is not connected. /// </exception> /// <exception cref="ServiceNotAuthenticatedException"> /// The <see cref="ImapClient"/> is not authenticated. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// The server's response contained unexpected tokens. /// </exception> /// <exception cref="ImapCommandException"> /// The server replied with a NO or BAD response. /// </exception> public override IEnumerable<IMailFolder> GetSubfolders (StatusItems items, bool subscribedOnly = false, CancellationToken cancellationToken = default (CancellationToken)) { CheckState (false, false); var pattern = EncodedName.Length > 0 ? EncodedName + DirectorySeparator : string.Empty; var children = new List<IMailFolder> (); var status = items != StatusItems.None; var list = new List<ImapFolder> (); var command = new StringBuilder (); var lsub = subscribedOnly; if (subscribedOnly) { if ((Engine.Capabilities & ImapCapabilities.ListExtended) != 0) { command.Append ("LIST (SUBSCRIBED)"); lsub = false; } else { command.Append ("LSUB"); } } else { command.Append ("LIST"); } command.Append (" \"\" %S"); if (!lsub) { if (items != StatusItems.None && (Engine.Capabilities & ImapCapabilities.ListStatus) != 0) { command.Append (" RETURN ("); if ((Engine.Capabilities & ImapCapabilities.ListExtended) != 0) { if (!subscribedOnly) command.Append ("SUBSCRIBED "); command.Append ("CHILDREN "); } command.AppendFormat ("STATUS ({0})", Engine.GetStatusQuery (items)); command.Append (')'); status = false; } else if ((Engine.Capabilities & ImapCapabilities.ListExtended) != 0) { command.Append (" RETURN ("); if (!subscribedOnly) command.Append ("SUBSCRIBED "); command.Append ("CHILDREN"); command.Append (')'); } } command.Append ("\r\n"); var ic = new ImapCommand (Engine, cancellationToken, null, command.ToString (), pattern + "%"); ic.RegisterUntaggedHandler (lsub ? "LSUB" : "LIST", ImapUtils.ParseFolderList); ic.UserData = list; Engine.QueueCommand (ic); Engine.Wait (ic); // Note: Some broken IMAP servers (*cough* SmarterMail 13.0 *cough*) return folders // that are not children of the folder we requested, so we need to filter those // folders out of the list that we'll be returning to our caller. // // See https://github.com/jstedfast/MailKit/issues/149 for more details. var prefix = FullName.Length > 0 ? FullName + DirectorySeparator : string.Empty; prefix = ImapUtils.CanonicalizeMailboxName (prefix, DirectorySeparator); foreach (var folder in list) { var canonicalFullName = ImapUtils.CanonicalizeMailboxName (folder.FullName, folder.DirectorySeparator); var canonicalName = ImapUtils.IsInbox (folder.FullName) ? "INBOX" : folder.Name; if (canonicalFullName != prefix + canonicalName) continue; if (lsub) { // the LSUB command does not send \Subscribed flags so we need to add them ourselves folder.Attributes |= FolderAttributes.Subscribed; } folder.ParentFolder = this; children.Add (folder); } ProcessResponseCodes (ic, null); if (ic.Response != ImapCommandResponse.Ok) throw ImapCommandException.Create (lsub ? "LSUB" : "LIST", ic); if (status) { for (int i = 0; i < children.Count; i++) children[i].Status (items, cancellationToken); } return children; }
/// <summary> /// Gets the folder for the specified path. /// </summary> /// <returns>The folder.</returns> /// <param name="path">The folder path.</param> /// <param name="cancellationToken">The cancellation token.</param> public ImapFolder GetFolder(string path, CancellationToken cancellationToken) { var encodedName = ImapEncoding.Encode (path); var list = new List<ImapFolder> (); ImapFolder folder; if (FolderCache.TryGetValue (encodedName, out folder)) return folder; var ic = new ImapCommand (this, cancellationToken, null, "LIST \"\" %S\r\n", encodedName); ic.RegisterUntaggedHandler ("LIST", ImapUtils.ParseFolderList); ic.UserData = list; QueueCommand (ic); Wait (ic); LookupParentFolders (list, cancellationToken); ProcessResponseCodes (ic); if (ic.Result != ImapCommandResult.Ok) throw new ImapCommandException ("LIST", ic.Result); return list.Count > 0 ? list[0] : null; }