Skip to content

Commit 306c50a

Browse files
committedSep 16, 2022
Make ImapFolder.Search(SearchOptions.None, query) work the same as Search(query)
Reduces code duplication and fixes issue #1437
1 parent 9a84021 commit 306c50a

File tree

3 files changed

+82
-194
lines changed

3 files changed

+82
-194
lines changed
 

‎MailKit/MailFolder.cs

+21-11
Original file line numberDiff line numberDiff line change
@@ -7817,7 +7817,12 @@ public int Count {
78177817
/// <exception cref="CommandException">
78187818
/// The command failed.
78197819
/// </exception>
7820-
public abstract IList<UniqueId> Search (SearchQuery query, CancellationToken cancellationToken = default (CancellationToken));
7820+
public virtual IList<UniqueId> Search (SearchQuery query, CancellationToken cancellationToken = default (CancellationToken))
7821+
{
7822+
var results = Search (SearchOptions.None, query, cancellationToken);
7823+
7824+
return results.UniqueIds;
7825+
}
78217826

78227827
/// <summary>
78237828
/// Asynchronously search the folder for messages matching the specified query.
@@ -7859,16 +7864,11 @@ public int Count {
78597864
/// <exception cref="CommandException">
78607865
/// The command failed.
78617866
/// </exception>
7862-
public virtual Task<IList<UniqueId>> SearchAsync (SearchQuery query, CancellationToken cancellationToken = default (CancellationToken))
7867+
public virtual async Task<IList<UniqueId>> SearchAsync (SearchQuery query, CancellationToken cancellationToken = default (CancellationToken))
78637868
{
7864-
if (query == null)
7865-
throw new ArgumentNullException (nameof (query));
7869+
var results = await SearchAsync (SearchOptions.None, query, cancellationToken).ConfigureAwait (false);
78667870

7867-
return Task.Factory.StartNew (() => {
7868-
lock (SyncRoot) {
7869-
return Search (query, cancellationToken);
7870-
}
7871-
}, cancellationToken, TaskCreationOptions.None, TaskScheduler.Default);
7871+
return results.UniqueIds;
78727872
}
78737873

78747874
/// <summary>
@@ -8247,7 +8247,12 @@ public int Count {
82478247
/// <exception cref="CommandException">
82488248
/// The command failed.
82498249
/// </exception>
8250-
public abstract IList<UniqueId> Sort (SearchQuery query, IList<OrderBy> orderBy, CancellationToken cancellationToken = default (CancellationToken));
8250+
public virtual IList<UniqueId> Sort (SearchQuery query, IList<OrderBy> orderBy, CancellationToken cancellationToken = default (CancellationToken))
8251+
{
8252+
var results = Sort (SearchOptions.None, query, orderBy, cancellationToken);
8253+
8254+
return results.UniqueIds;
8255+
}
82518256

82528257
/// <summary>
82538258
/// Asynchronously sort messages matching the specified query.
@@ -8297,7 +8302,12 @@ public int Count {
82978302
/// <exception cref="CommandException">
82988303
/// The command failed.
82998304
/// </exception>
8300-
public abstract Task<IList<UniqueId>> SortAsync (SearchQuery query, IList<OrderBy> orderBy, CancellationToken cancellationToken = default (CancellationToken));
8305+
public virtual async Task<IList<UniqueId>> SortAsync (SearchQuery query, IList<OrderBy> orderBy, CancellationToken cancellationToken = default (CancellationToken))
8306+
{
8307+
var results = await SortAsync (SearchOptions.None, query, orderBy, cancellationToken).ConfigureAwait (false);
8308+
8309+
return results.UniqueIds;
8310+
}
83018311

83028312
/// <summary>
83038313
/// Sort messages matching the specified query.

‎MailKit/Net/Imap/ImapFolder.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -3698,19 +3698,19 @@ async Task ExpungeAsync (IList<UniqueId> uids, bool doAsync, CancellationToken c
36983698
if ((Engine.Capabilities & ImapCapabilities.UidPlus) == 0) {
36993699
// get the list of messages marked for deletion that should not be expunged
37003700
var query = SearchQuery.Deleted.And (SearchQuery.Not (SearchQuery.Uids (uids)));
3701-
var unmark = await SearchAsync (query, doAsync, false, cancellationToken).ConfigureAwait (false);
3701+
var unmark = await SearchAsync (SearchOptions.None, query, doAsync, false, cancellationToken).ConfigureAwait (false);
37023702

37033703
if (unmark.Count > 0) {
37043704
// clear the \Deleted flag on all messages except the ones that are to be expunged
3705-
await StoreAsync (unmark, RemoveDeletedFlag, doAsync, cancellationToken).ConfigureAwait (false);
3705+
await StoreAsync (unmark.UniqueIds, RemoveDeletedFlag, doAsync, cancellationToken).ConfigureAwait (false);
37063706
}
37073707

37083708
// expunge the folder
37093709
await ExpungeAsync (doAsync, cancellationToken).ConfigureAwait (false);
37103710

37113711
if (unmark.Count > 0) {
37123712
// restore the \Deleted flags
3713-
await StoreAsync (unmark, AddDeletedFlag, doAsync, cancellationToken).ConfigureAwait (false);
3713+
await StoreAsync (unmark.UniqueIds, AddDeletedFlag, doAsync, cancellationToken).ConfigureAwait (false);
37143714
}
37153715

37163716
return;

‎MailKit/Net/Imap/ImapFolderSearch.cs

+58-180
Original file line numberDiff line numberDiff line change
@@ -748,189 +748,58 @@ async Task<SearchResults> SearchAsync (string query, bool doAsync, CancellationT
748748
return SearchAsync (query, true, cancellationToken);
749749
}
750750

751-
async Task<IList<UniqueId>> SearchAsync (SearchQuery query, bool doAsync, bool retry, CancellationToken cancellationToken)
752-
{
753-
var args = new List<object> ();
754-
string charset;
755-
756-
if (query == null)
757-
throw new ArgumentNullException (nameof (query));
758-
759-
CheckState (true, false);
760-
761-
var optimized = query.Optimize (new ImapSearchQueryOptimizer ());
762-
var expr = BuildQueryExpression (optimized, args, out charset);
763-
var command = "UID SEARCH ";
764-
765-
if ((Engine.Capabilities & ImapCapabilities.ESearch) != 0)
766-
command += "RETURN (ALL) ";
767-
768-
if (charset != null && args.Count > 0 && !Engine.UTF8Enabled)
769-
command += "CHARSET " + charset + " ";
770-
771-
command += expr + "\r\n";
772-
773-
var ic = new ImapCommand (Engine, cancellationToken, this, command, args.ToArray ());
774-
if ((Engine.Capabilities & ImapCapabilities.ESearch) != 0)
775-
ic.RegisterUntaggedHandler ("ESEARCH", ESearchMatchesAsync);
776-
777-
// Note: always register the untagged SEARCH handler because some servers will brokenly
778-
// respond with "* SEARCH ..." instead of "* ESEARCH ..." even when using the extended
779-
// search syntax.
780-
ic.RegisterUntaggedHandler ("SEARCH", SearchMatchesAsync);
781-
ic.UserData = new SearchResults (UidValidity, SortOrder.Ascending);
782-
783-
Engine.QueueCommand (ic);
784-
785-
await Engine.RunAsync (ic, doAsync).ConfigureAwait (false);
786-
787-
ProcessResponseCodes (ic, null);
788-
789-
if (ic.Response != ImapCommandResponse.Ok) {
790-
if (retry && IsBadCharset (ic, charset))
791-
return await SearchAsync (query, doAsync, false, cancellationToken).ConfigureAwait (false);
792-
793-
throw ImapCommandException.Create ("SEARCH", ic);
794-
}
795-
796-
return ((SearchResults) ic.UserData).UniqueIds;
797-
}
798-
799-
/// <summary>
800-
/// Search the folder for messages matching the specified query.
801-
/// </summary>
802-
/// <remarks>
803-
/// The returned array of unique identifiers can be used with methods such as
804-
/// <see cref="IMailFolder.GetMessage(UniqueId,CancellationToken,ITransferProgress)"/>.
805-
/// </remarks>
806-
/// <returns>An array of matching UIDs.</returns>
807-
/// <param name="query">The search query.</param>
808-
/// <param name="cancellationToken">The cancellation token.</param>
809-
/// <exception cref="System.ArgumentNullException">
810-
/// <paramref name="query"/> is <c>null</c>.
811-
/// </exception>
812-
/// <exception cref="System.NotSupportedException">
813-
/// One or more search terms in the <paramref name="query"/> are not supported by the IMAP server.
814-
/// </exception>
815-
/// <exception cref="System.ObjectDisposedException">
816-
/// The <see cref="ImapClient"/> has been disposed.
817-
/// </exception>
818-
/// <exception cref="ServiceNotConnectedException">
819-
/// The <see cref="ImapClient"/> is not connected.
820-
/// </exception>
821-
/// <exception cref="ServiceNotAuthenticatedException">
822-
/// The <see cref="ImapClient"/> is not authenticated.
823-
/// </exception>
824-
/// <exception cref="FolderNotOpenException">
825-
/// The <see cref="ImapFolder"/> is not currently open.
826-
/// </exception>
827-
/// <exception cref="System.OperationCanceledException">
828-
/// The operation was canceled via the cancellation token.
829-
/// </exception>
830-
/// <exception cref="System.IO.IOException">
831-
/// An I/O error occurred.
832-
/// </exception>
833-
/// <exception cref="ImapProtocolException">
834-
/// The server's response contained unexpected tokens.
835-
/// </exception>
836-
/// <exception cref="ImapCommandException">
837-
/// The server replied with a NO or BAD response.
838-
/// </exception>
839-
public override IList<UniqueId> Search (SearchQuery query, CancellationToken cancellationToken = default (CancellationToken))
840-
{
841-
return SearchAsync (query, false, true, cancellationToken).GetAwaiter ().GetResult ();
842-
}
843-
844-
/// <summary>
845-
/// Asynchronously search the folder for messages matching the specified query.
846-
/// </summary>
847-
/// <remarks>
848-
/// The returned array of unique identifiers can be used with methods such as
849-
/// <see cref="IMailFolder.GetMessage(UniqueId,CancellationToken,ITransferProgress)"/>.
850-
/// </remarks>
851-
/// <returns>An array of matching UIDs.</returns>
852-
/// <param name="query">The search query.</param>
853-
/// <param name="cancellationToken">The cancellation token.</param>
854-
/// <exception cref="System.ArgumentNullException">
855-
/// <paramref name="query"/> is <c>null</c>.
856-
/// </exception>
857-
/// <exception cref="System.NotSupportedException">
858-
/// One or more search terms in the <paramref name="query"/> are not supported by the IMAP server.
859-
/// </exception>
860-
/// <exception cref="System.ObjectDisposedException">
861-
/// The <see cref="ImapClient"/> has been disposed.
862-
/// </exception>
863-
/// <exception cref="ServiceNotConnectedException">
864-
/// The <see cref="ImapClient"/> is not connected.
865-
/// </exception>
866-
/// <exception cref="ServiceNotAuthenticatedException">
867-
/// The <see cref="ImapClient"/> is not authenticated.
868-
/// </exception>
869-
/// <exception cref="FolderNotOpenException">
870-
/// The <see cref="ImapFolder"/> is not currently open.
871-
/// </exception>
872-
/// <exception cref="System.OperationCanceledException">
873-
/// The operation was canceled via the cancellation token.
874-
/// </exception>
875-
/// <exception cref="System.IO.IOException">
876-
/// An I/O error occurred.
877-
/// </exception>
878-
/// <exception cref="ImapProtocolException">
879-
/// The server's response contained unexpected tokens.
880-
/// </exception>
881-
/// <exception cref="ImapCommandException">
882-
/// The server replied with a NO or BAD response.
883-
/// </exception>
884-
public override Task<IList<UniqueId>> SearchAsync (SearchQuery query, CancellationToken cancellationToken = default (CancellationToken))
885-
{
886-
return SearchAsync (query, true, true, cancellationToken);
887-
}
888-
889751
async Task<SearchResults> SearchAsync (SearchOptions options, SearchQuery query, bool doAsync, bool retry, CancellationToken cancellationToken)
890752
{
891-
var args = new List<object> ();
892-
string charset;
893-
894753
if (query == null)
895754
throw new ArgumentNullException (nameof (query));
896755

897756
CheckState (true, false);
898757

899-
if ((Engine.Capabilities & ImapCapabilities.ESearch) == 0)
758+
if (options != SearchOptions.None && (Engine.Capabilities & ImapCapabilities.ESearch) == 0)
900759
throw new NotSupportedException ("The IMAP server does not support the ESEARCH extension.");
901760

761+
var args = new List<object> ();
902762
var optimized = query.Optimize (new ImapSearchQueryOptimizer ());
903-
var expr = BuildQueryExpression (optimized, args, out charset);
904-
var command = "UID SEARCH RETURN (";
905-
906-
if (options != SearchOptions.All && options != 0) {
907-
if ((options & SearchOptions.All) != 0)
908-
command += "ALL ";
909-
if ((options & SearchOptions.Relevancy) != 0)
910-
command += "RELEVANCY ";
911-
if ((options & SearchOptions.Count) != 0)
912-
command += "COUNT ";
913-
if ((options & SearchOptions.Min) != 0)
914-
command += "MIN ";
915-
if ((options & SearchOptions.Max) != 0)
916-
command += "MAX ";
917-
command = command.TrimEnd ();
763+
var expr = BuildQueryExpression (optimized, args, out string charset);
764+
var command = "UID SEARCH ";
765+
766+
if ((Engine.Capabilities & ImapCapabilities.ESearch) != 0) {
767+
command += "RETURN (";
768+
769+
if (options != SearchOptions.All && options != SearchOptions.None) {
770+
if ((options & SearchOptions.All) != 0)
771+
command += "ALL ";
772+
if ((options & SearchOptions.Relevancy) != 0)
773+
command += "RELEVANCY ";
774+
if ((options & SearchOptions.Count) != 0)
775+
command += "COUNT ";
776+
if ((options & SearchOptions.Min) != 0)
777+
command += "MIN ";
778+
if ((options & SearchOptions.Max) != 0)
779+
command += "MAX ";
780+
command = command.TrimEnd ();
781+
} else {
782+
command += "ALL";
783+
}
784+
785+
command += ") ";
918786
}
919-
command += ") ";
920787

921788
if (charset != null && args.Count > 0 && !Engine.UTF8Enabled)
922789
command += "CHARSET " + charset + " ";
923790

924791
command += expr + "\r\n";
925792

926793
var ic = new ImapCommand (Engine, cancellationToken, this, command, args.ToArray ());
927-
ic.RegisterUntaggedHandler ("ESEARCH", ESearchMatchesAsync);
794+
ic.UserData = new SearchResults (UidValidity, SortOrder.Ascending);
795+
796+
if ((Engine.Capabilities & ImapCapabilities.ESearch) != 0)
797+
ic.RegisterUntaggedHandler ("ESEARCH", ESearchMatchesAsync);
928798

929799
// Note: always register the untagged SEARCH handler because some servers will brokenly
930800
// respond with "* SEARCH ..." instead of "* ESEARCH ..." even when using the extended
931801
// search syntax.
932802
ic.RegisterUntaggedHandler ("SEARCH", SearchMatchesAsync);
933-
ic.UserData = new SearchResults (UidValidity);
934803

935804
Engine.QueueCommand (ic);
936805

@@ -1338,9 +1207,6 @@ async Task<IList<UniqueId>> SortAsync (SearchQuery query, IList<OrderBy> orderBy
13381207

13391208
async Task<SearchResults> SortAsync (SearchOptions options, SearchQuery query, IList<OrderBy> orderBy, bool doAsync, bool retry, CancellationToken cancellationToken)
13401209
{
1341-
var args = new List<object> ();
1342-
string charset;
1343-
13441210
if (query == null)
13451211
throw new ArgumentNullException (nameof (query));
13461212

@@ -1352,35 +1218,47 @@ async Task<SearchResults> SortAsync (SearchOptions options, SearchQuery query, I
13521218

13531219
CheckState (true, false);
13541220

1355-
if ((Engine.Capabilities & ImapCapabilities.ESort) == 0)
1221+
if (options != SearchOptions.None && (Engine.Capabilities & ImapCapabilities.ESort) == 0)
13561222
throw new NotSupportedException ("The IMAP server does not support the ESORT extension.");
13571223

1224+
var args = new List<object> ();
13581225
var optimized = query.Optimize (new ImapSearchQueryOptimizer ());
1359-
var expr = BuildQueryExpression (optimized, args, out charset);
1226+
var expr = BuildQueryExpression (optimized, args, out string charset);
13601227
var order = BuildSortOrder (orderBy);
1228+
var command = "UID SORT ";
13611229

1362-
var command = "UID SORT RETURN (";
1363-
if (options != SearchOptions.All && options != 0) {
1364-
if ((options & SearchOptions.All) != 0)
1365-
command += "ALL ";
1366-
if ((options & SearchOptions.Relevancy) != 0)
1367-
command += "RELEVANCY ";
1368-
if ((options & SearchOptions.Count) != 0)
1369-
command += "COUNT ";
1370-
if ((options & SearchOptions.Min) != 0)
1371-
command += "MIN ";
1372-
if ((options & SearchOptions.Max) != 0)
1373-
command += "MAX ";
1374-
command = command.TrimEnd ();
1230+
if ((Engine.Capabilities & ImapCapabilities.ESort) != 0) {
1231+
command += "RETURN (";
1232+
1233+
if (options != SearchOptions.All && options != SearchOptions.None) {
1234+
if ((options & SearchOptions.All) != 0)
1235+
command += "ALL ";
1236+
if ((options & SearchOptions.Relevancy) != 0)
1237+
command += "RELEVANCY ";
1238+
if ((options & SearchOptions.Count) != 0)
1239+
command += "COUNT ";
1240+
if ((options & SearchOptions.Min) != 0)
1241+
command += "MIN ";
1242+
if ((options & SearchOptions.Max) != 0)
1243+
command += "MAX ";
1244+
command = command.TrimEnd ();
1245+
} else {
1246+
command += "ALL";
1247+
}
1248+
1249+
command += ") ";
13751250
}
1376-
command += ") ";
13771251

13781252
command += order + " " + (charset ?? "US-ASCII") + " " + expr + "\r\n";
13791253

13801254
var ic = new ImapCommand (Engine, cancellationToken, this, command, args.ToArray ());
1381-
ic.RegisterUntaggedHandler ("ESEARCH", ESearchMatchesAsync);
13821255
ic.UserData = new SearchResults (UidValidity);
13831256

1257+
if ((Engine.Capabilities & ImapCapabilities.ESort) != 0)
1258+
ic.RegisterUntaggedHandler ("ESEARCH", ESearchMatchesAsync);
1259+
else
1260+
ic.RegisterUntaggedHandler ("SORT", SearchMatchesAsync);
1261+
13841262
Engine.QueueCommand (ic);
13851263

13861264
await Engine.RunAsync (ic, doAsync).ConfigureAwait (false);

0 commit comments

Comments
 (0)
Please sign in to comment.