Skip to content

Commit

Permalink
Added new IMailFolder.GetStream() methods that just take a uid/index …
Browse files Browse the repository at this point in the history
…and a BodyPart
  • Loading branch information
jstedfast committed Aug 25, 2023
1 parent 1ac7170 commit ce0de57
Show file tree
Hide file tree
Showing 3 changed files with 355 additions and 0 deletions.
68 changes: 68 additions & 0 deletions Documentation/Examples/ImapBodyPartExamples.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,74 @@ public static void DownloadBodyParts (string baseDirectory)
}
#endregion

#region GetBodyPartStreamsByUniqueId
public static void CacheBodyParts (string baseDirectory)
{
using (var client = new ImapClient ()) {
client.Connect ("imap.gmail.com", 993, SecureSocketOptions.SslOnConnect);

client.Authenticate ("username", "password");

client.Inbox.Open (FolderAccess.ReadOnly);

// search for messages where the Subject header contains either "MimeKit" or "MailKit"
var query = SearchQuery.SubjectContains ("MimeKit").Or (SearchQuery.SubjectContains ("MailKit"));
var uids = client.Inbox.Search (query);

// fetch summary information for the search results (we will want the UID and the BODYSTRUCTURE
// of each message so that we can extract the text body and the attachments)
var items = client.Inbox.Fetch (uids, MessageSummaryItems.UniqueId | MessageSummaryItems.BodyStructure);

foreach (var item in items) {
// determine a directory to save stuff in
var directory = Path.Combine (baseDirectory, item.UniqueId.ToString ());

// create the directory
Directory.CreateDirectory (directory);

// IMessageSummary.TextBody is a convenience property that finds the 'text/plain' body part for us
var bodyPart = item.TextBody;

if (bodyPart != null) {
// cache the raw 'text/plain' MIME part for later use
using (var stream = client.Inbox.GetStream (item.UniqueId, bodyPart)) {
var path = path.Combine (directory, bodyPart.PartSpecifier);

using (var output = File.Create (path))
stream.CopyTo (output);
}
}

// IMessageSummary.HtmlBody is a convenience property that finds the 'text/html' body part for us
bodyPart = item.HtmlBody;

if (bodyPart != null) {
// cache the raw 'text/html' MIME part for later use
using (var stream = client.Inbox.GetStream (item.UniqueId, bodyPart)) {
var path = path.Combine (directory, bodyPart.PartSpecifier);

using (var output = File.Create (path))
stream.CopyTo (output);
}
}

// now iterate over all of the attachments and save them to disk
foreach (var attachment in item.Attachments) {
// cache the raw MIME attachment just like we did with the body
using (var stream = client.Inbox.GetStream (item.UniqueId, attachment)) {
var path = path.Combine (directory, attachment.PartSpecifier);

using (var output = File.Create (path))
stream.CopyTo (output);
}
}
}

client.Disconnect (true);
}
}
#endregion

#region GetBodyPartStreamsByUniqueIdAndSpecifier
public static void CacheBodyParts (string baseDirectory)
{
Expand Down
58 changes: 58 additions & 0 deletions MailKit/IMailFolder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1851,6 +1851,64 @@ public interface IMailFolder : IEnumerable<MimeMessage>
/// <param name="progress">The progress reporting mechanism.</param>
Task<Stream> GetStreamAsync (int index, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null);

/// <summary>
/// Get a body part as a stream.
/// </summary>
/// <remarks>
/// Gets a body part as a stream.
/// </remarks>
/// <example>
/// <code language="c#" source="Examples\ImapBodyPartExamples.cs" region="GetBodyPartStreamsByUniqueId"/>
/// </example>
/// <returns>The body part stream.</returns>
/// <param name="uid">The UID of the message.</param>
/// <param name="part">The desired body part.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress reporting mechanism.</param>
Stream GetStream (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null);

/// <summary>
/// Asynchronously get a body part as a stream.
/// </summary>
/// <remarks>
/// Asynchronously gets a body part as a stream.
/// </remarks>
/// <example>
/// <code language="c#" source="Examples\ImapBodyPartExamples.cs" region="GetBodyPartStreamsByUniqueId"/>
/// </example>
/// <returns>The body part stream.</returns>
/// <param name="uid">The UID of the message.</param>
/// <param name="part">The desired body part.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress reporting mechanism.</param>
Task<Stream> GetStreamAsync (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null);

/// <summary>
/// Get a body part as a stream.
/// </summary>
/// <remarks>
/// Gets a body part as a stream.
/// </remarks>
/// <returns>The body part stream.</returns>
/// <param name="index">The index of the message.</param>
/// <param name="part">The desired body part.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress reporting mechanism.</param>
Stream GetStream (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null);

/// <summary>
/// Asynchronously get a body part as a stream.
/// </summary>
/// <remarks>
/// Asynchronously gets a body part as a stream.
/// </remarks>
/// <returns>The body part stream.</returns>
/// <param name="index">The index of the message.</param>
/// <param name="part">The desired body part.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress reporting mechanism.</param>
Task<Stream> GetStreamAsync (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null);

/// <summary>
/// Get a substream of the specified body part.
/// </summary>
Expand Down
229 changes: 229 additions & 0 deletions MailKit/MailFolder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5671,6 +5671,232 @@ public virtual Task MoveToAsync (int index, IMailFolder destination, Cancellatio
/// </exception>
public abstract Task<Stream> GetStreamAsync (int index, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null);

/// <summary>
/// Get a body part as a stream.
/// </summary>
/// <remarks>
/// Gets a body part as a stream.
/// </remarks>
/// <example>
/// <code language="c#" source="Examples\ImapBodyPartExamples.cs" region="GetBodyPartStreamsByUniqueId"/>
/// </example>
/// <returns>The body part stream.</returns>
/// <param name="uid">The UID of the message.</param>
/// <param name="part">The desired body part.</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="part"/> is <c>null</c>.
/// </exception>
/// <exception cref="System.ObjectDisposedException">
/// The <see cref="IMailStore"/> has been disposed.
/// </exception>
/// <exception cref="ServiceNotConnectedException">
/// The <see cref="IMailStore"/> is not connected.
/// </exception>
/// <exception cref="ServiceNotAuthenticatedException">
/// The <see cref="IMailStore"/> is not authenticated.
/// </exception>
/// <exception cref="FolderNotOpenException">
/// The folder is not currently open.
/// </exception>
/// <exception cref="MessageNotFoundException">
/// The <see cref="IMailStore"/> 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="ProtocolException">
/// The server's response contained unexpected tokens.
/// </exception>
/// <exception cref="CommandException">
/// The command failed.
/// </exception>
public virtual Stream GetStream (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null)
{
if (!uid.IsValid)
throw new ArgumentException ("The uid is invalid.", nameof (uid));

if (part == null)
throw new ArgumentNullException (nameof (part));

return GetStream (uid, part.PartSpecifier, cancellationToken, progress);
}

/// <summary>
/// Asynchronously get a body part as a stream.
/// </summary>
/// <remarks>
/// Asynchronously gets a body part as a stream.
/// </remarks>
/// <example>
/// <code language="c#" source="Examples\ImapBodyPartExamples.cs" region="GetBodyPartStreamsByUniqueId"/>
/// </example>
/// <returns>The body part stream.</returns>
/// <param name="uid">The UID of the message.</param>
/// <param name="part">The desired body part.</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="part"/> is <c>null</c>.
/// </exception>
/// <exception cref="System.ObjectDisposedException">
/// The <see cref="IMailStore"/> has been disposed.
/// </exception>
/// <exception cref="ServiceNotConnectedException">
/// The <see cref="IMailStore"/> is not connected.
/// </exception>
/// <exception cref="ServiceNotAuthenticatedException">
/// The <see cref="IMailStore"/> is not authenticated.
/// </exception>
/// <exception cref="FolderNotOpenException">
/// The folder is not currently open.
/// </exception>
/// <exception cref="MessageNotFoundException">
/// The <see cref="IMailStore"/> 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="ProtocolException">
/// The server's response contained unexpected tokens.
/// </exception>
/// <exception cref="CommandException">
/// The command failed.
/// </exception>
public virtual Task<Stream> GetStreamAsync (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null)
{
if (!uid.IsValid)
throw new ArgumentException ("The uid is invalid.", nameof (uid));

if (part == null)
throw new ArgumentNullException (nameof (part));

return GetStreamAsync (uid, part.PartSpecifier, cancellationToken, progress);
}

/// <summary>
/// Get a body part as a stream.
/// </summary>
/// <remarks>
/// Gets a body part as a stream.
/// </remarks>
/// <returns>The body part stream.</returns>
/// <param name="index">The index of the message.</param>
/// <param name="part">The desired body part.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress reporting mechanism.</param>
/// <exception cref="System.ArgumentNullException">
/// <paramref name="part"/> is <c>null</c>.
/// </exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// <para><paramref name="index"/> is out of range.</para>
/// </exception>
/// <exception cref="System.ObjectDisposedException">
/// The <see cref="IMailStore"/> has been disposed.
/// </exception>
/// <exception cref="ServiceNotConnectedException">
/// The <see cref="IMailStore"/> is not connected.
/// </exception>
/// <exception cref="ServiceNotAuthenticatedException">
/// The <see cref="IMailStore"/> is not authenticated.
/// </exception>
/// <exception cref="FolderNotOpenException">
/// The folder is not currently open.
/// </exception>
/// <exception cref="MessageNotFoundException">
/// The <see cref="IMailStore"/> 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="ProtocolException">
/// The server's response contained unexpected tokens.
/// </exception>
/// <exception cref="CommandException">
/// The command failed.
/// </exception>
public virtual Stream GetStream (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null)
{
if (index < 0 || index >= Count)
throw new ArgumentOutOfRangeException (nameof (index));

if (part == null)
throw new ArgumentNullException (nameof (part));

return GetStream (index, part.PartSpecifier, cancellationToken, progress);
}

/// <summary>
/// Asynchronously get a body part as a stream.
/// </summary>
/// <remarks>
/// Asynchronously gets a body part as a stream.
/// </remarks>
/// <returns>The body part stream.</returns>
/// <param name="index">The index of the message.</param>
/// <param name="part">The desired body part.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress reporting mechanism.</param>
/// <exception cref="System.ArgumentNullException">
/// <paramref name="part"/> is <c>null</c>.
/// </exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// <para><paramref name="index"/> is out of range.</para>
/// </exception>
/// <exception cref="System.ObjectDisposedException">
/// The <see cref="IMailStore"/> has been disposed.
/// </exception>
/// <exception cref="ServiceNotConnectedException">
/// The <see cref="IMailStore"/> is not connected.
/// </exception>
/// <exception cref="ServiceNotAuthenticatedException">
/// The <see cref="IMailStore"/> is not authenticated.
/// </exception>
/// <exception cref="FolderNotOpenException">
/// The folder is not currently open.
/// </exception>
/// <exception cref="MessageNotFoundException">
/// The <see cref="IMailStore"/> 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="ProtocolException">
/// The server's response contained unexpected tokens.
/// </exception>
/// <exception cref="CommandException">
/// The command failed.
/// </exception>
public virtual Task<Stream> GetStreamAsync (int index, BodyPart part, CancellationToken cancellationToken = default, ITransferProgress progress = null)
{
if (index < 0 || index >= Count)
throw new ArgumentOutOfRangeException (nameof (index));

if (part == null)
throw new ArgumentNullException (nameof (part));

return GetStreamAsync (index, part.PartSpecifier, cancellationToken, progress);
}

/// <summary>
/// Get a substream of the specified body part.
/// </summary>
Expand Down Expand Up @@ -5798,6 +6024,9 @@ public virtual Stream GetStream (UniqueId uid, BodyPart part, int offset, int co
/// </exception>
public virtual Task<Stream> GetStreamAsync (UniqueId uid, BodyPart part, int offset, int count, CancellationToken cancellationToken = default, ITransferProgress progress = null)
{
if (!uid.IsValid)
throw new ArgumentException ("The uid is invalid.", nameof (uid));

if (part == null)
throw new ArgumentNullException (nameof (part));

Expand Down

0 comments on commit ce0de57

Please sign in to comment.