Getting a sent MailMessage into the "Sent Folder"
Asked Answered
D

5

17

I'm sending MailMessages with an SmtpClient (being delivered successfully) using an Exchange Server but would like my sent emails to go to the Sent Folder of the email address I'm sending them from (not happening).

using (var mailMessage = new MailMessage("[email protected]", "[email protected]", "subject", "body"))
{
    var smtpClient = new SmtpClient("SmtpHost")
    {
        EnableSsl = false,
        DeliveryMethod = SmtpDeliveryMethod.Network
    };

    // Apply credentials
    smtpClient.Credentials = new NetworkCredential("smtpUsername", "smtpPassword");

    // Send
    smtpClient.Send(mailMessage);
}

Is there a configuration I'm missing that will ensure all of my sent emails from "[email protected]" arrive in their Sent Folder?

Domoniquedomph answered 18/3, 2010 at 15:49 Comment(0)
R
12

I'm guessing that your requirement is mainly oriented around giving the users visibility to what emails have been sent. The sent items folder would be one method to allow this to occur. In the past, I've solved this problem by adding a BCC Address that would literally send the email directly to either a distribution list, user, or shared mailbox that allowed the users to review what had been sent.

Try this with an outlook rule of some kind to move the item to their sent items folder marked as read...

using (var mailMessage = new MailMessage(
        "[email protected]", 
        "[email protected]", 
        "",
        "[email protected]",
        "subject", 
        "body"))
{
    var smtpClient = new SmtpClient("SmtpHost")
    {
        EnableSsl = false,
        DeliveryMethod = SmtpDeliveryMethod.Network
    };

    // Apply credentials
    smtpClient.Credentials = new NetworkCredential("smtpUsername", "smtpPassword");

    // Send
    smtpClient.Send(mailMessage);
}
Rath answered 18/3, 2010 at 15:56 Comment(2)
Thanks for your response! That's definitely a likely consideration now as it's literally just confirmation I need...Domoniquedomph
The Exchange service answer by LachlanB below is the real answer. This is a bit of a hack.Farmland
R
22

I have done this, so for completeness here's how to do it properly. Using the managed exchange web service ( http://msdn.microsoft.com/en-us/library/dd633709%28EXCHG.80%29.aspx ):

ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);

// In case you have a dodgy SSL certificate:
System.Net.ServicePointManager.ServerCertificateValidationCallback =
            delegate(Object obj, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
            {
                return true;
            };

service.Credentials = new WebCredentials("username", "password", "MYDOMAIN");
service.Url = new Uri("https://exchangebox/EWS/Exchange.asmx");

EmailMessage em = new EmailMessage(service);
em.Subject = "example email";
em.Body = new MessageBody("hello world");
em.Sender = new Microsoft.Exchange.WebServices.Data.EmailAddress("[email protected]");
em.ToRecipients.Add(new Microsoft.Exchange.WebServices.Data.EmailAddress("[email protected]"));

// Send the email and put it into the SentItems:
em.SendAndSaveCopy(WellKnownFolderName.SentItems);
Rosyrot answered 1/2, 2012 at 5:57 Comment(3)
this should be the top answerInsular
@Insular - I believe this solution, while elegant, ties the user to an Exchange provider. The OP is not currently limited in that way since they're using SmtpClient.Sticker
@Sticker he mentioned that he was using Exchange and there's no way to do that with SmtpClientRosyrot
R
12

I'm guessing that your requirement is mainly oriented around giving the users visibility to what emails have been sent. The sent items folder would be one method to allow this to occur. In the past, I've solved this problem by adding a BCC Address that would literally send the email directly to either a distribution list, user, or shared mailbox that allowed the users to review what had been sent.

Try this with an outlook rule of some kind to move the item to their sent items folder marked as read...

using (var mailMessage = new MailMessage(
        "[email protected]", 
        "[email protected]", 
        "",
        "[email protected]",
        "subject", 
        "body"))
{
    var smtpClient = new SmtpClient("SmtpHost")
    {
        EnableSsl = false,
        DeliveryMethod = SmtpDeliveryMethod.Network
    };

    // Apply credentials
    smtpClient.Credentials = new NetworkCredential("smtpUsername", "smtpPassword");

    // Send
    smtpClient.Send(mailMessage);
}
Rath answered 18/3, 2010 at 15:56 Comment(2)
Thanks for your response! That's definitely a likely consideration now as it's literally just confirmation I need...Domoniquedomph
The Exchange service answer by LachlanB below is the real answer. This is a bit of a hack.Farmland
H
2

I have been searching for an answer to this question but without relying on an Exchange Server, and instead use an IMAP server. I don't know if this falls out of the scope of the question but I found it searching for "Getting a sent MailMessage into the Sent Folder" which was my problem in the first place.

Haven't found a direct answer anywhere I built my own solution based on:

I'm implementing the save method as an extension to smtpClient so, instead of of .Send() we'll be using .SendAndSaveMessageToIMAP().

public static class SmtpClientExtensions
{
    static System.IO.StreamWriter sw = null;
    static System.Net.Sockets.TcpClient tcpc = null;
    static System.Net.Security.SslStream ssl = null;
    static string path;
    static int bytes = -1;
    static byte[] buffer;
    static System.Text.StringBuilder sb = new System.Text.StringBuilder();
    static byte[] dummy;

    /// <summary>
    /// Communication with server
    /// </summary>
    /// <param name="command">The command beeing sent</param>
    private static void SendCommandAndReceiveResponse(string command)
    {
        try
        {
            if (command != "")
            {
                if (tcpc.Connected)
                {
                    dummy = System.Text.Encoding.ASCII.GetBytes(command);
                    ssl.Write(dummy, 0, dummy.Length);
                }
                else
                {
                    throw new System.ApplicationException("TCP CONNECTION DISCONNECTED");
                }
            }
            ssl.Flush();

            buffer = new byte[2048];
            bytes = ssl.Read(buffer, 0, 2048);
            sb.Append(System.Text.Encoding.ASCII.GetString(buffer));

            sw.WriteLine(sb.ToString());
            sb = new System.Text.StringBuilder();
        }
        catch (System.Exception ex)
        {
            throw new System.ApplicationException(ex.Message);
        }
    }

    /// <summary>
    /// Saving a mail message before beeing sent by the SMTP client
    /// </summary>
    /// <param name="self">The caller</param>
    /// <param name="imapServer">The address of the IMAP server</param>
    /// <param name="imapPort">The port of the IMAP server</param>
    /// <param name="userName">The username to log on to the IMAP server</param>
    /// <param name="password">The password to log on to the IMAP server</param>
    /// <param name="sentFolderName">The name of the folder where the message will be saved</param>
    /// <param name="mailMessage">The message being saved</param>
    public static void SendAndSaveMessageToIMAP(this System.Net.Mail.SmtpClient self, System.Net.Mail.MailMessage mailMessage, string imapServer, int imapPort, string userName, string password, string sentFolderName)
    {
        try
        {
            path = System.Environment.CurrentDirectory + "\\emailresponse.txt";

            if (System.IO.File.Exists(path))
                System.IO.File.Delete(path);

            sw = new System.IO.StreamWriter(System.IO.File.Create(path));

            tcpc = new System.Net.Sockets.TcpClient(imapServer, imapPort);

            ssl = new System.Net.Security.SslStream(tcpc.GetStream());
            ssl.AuthenticateAsClient(imapServer);
            SendCommandAndReceiveResponse("");

            SendCommandAndReceiveResponse(string.Format("$ LOGIN {1} {2}  {0}", System.Environment.NewLine, userName, password));

            using (var m = mailMessage.RawMessage())
            {
                m.Position = 0;
                var sr = new System.IO.StreamReader(m);
                var myStr = sr.ReadToEnd();
                SendCommandAndReceiveResponse(string.Format("$ APPEND {1} (\\Seen) {{{2}}}{0}", System.Environment.NewLine, sentFolderName, myStr.Length));
                SendCommandAndReceiveResponse(string.Format("{1}{0}", System.Environment.NewLine, myStr));
            }
            SendCommandAndReceiveResponse(string.Format("$ LOGOUT{0}", System.Environment.NewLine));
        }
        catch (System.Exception ex)
        {
            System.Diagnostics.Debug.WriteLine("error: " + ex.Message);
        }
        finally
        {
            if (sw != null)
            {
                sw.Close();
                sw.Dispose();
            }
            if (ssl != null)
            {
                ssl.Close();
                ssl.Dispose();
            }
            if (tcpc != null)
            {
                tcpc.Close();
            }
        }

        self.Send(mailMessage);
    }
}
public static class MailMessageExtensions
{
    private static readonly System.Reflection.BindingFlags Flags = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic;
    private static readonly System.Type MailWriter = typeof(System.Net.Mail.SmtpClient).Assembly.GetType("System.Net.Mail.MailWriter");
    private static readonly System.Reflection.ConstructorInfo MailWriterConstructor = MailWriter.GetConstructor(Flags, null, new[] { typeof(System.IO.Stream) }, null);
    private static readonly System.Reflection.MethodInfo CloseMethod = MailWriter.GetMethod("Close", Flags);
    private static readonly System.Reflection.MethodInfo SendMethod = typeof(System.Net.Mail.MailMessage).GetMethod("Send", Flags);

    /// <summary>
    /// A little hack to determine the number of parameters that we
    /// need to pass to the SaveMethod.
    /// </summary>
    private static readonly bool IsRunningInDotNetFourPointFive = SendMethod.GetParameters().Length == 3;

    /// <summary>
    /// The raw contents of this MailMessage as a MemoryStream.
    /// </summary>
    /// <param name="self">The caller.</param>
    /// <returns>A MemoryStream with the raw contents of this MailMessage.</returns>
    public static System.IO.MemoryStream RawMessage(this System.Net.Mail.MailMessage self)
    {
        var result = new System.IO.MemoryStream();
        var mailWriter = MailWriterConstructor.Invoke(new object[] { result });
        SendMethod.Invoke(self, Flags, null, IsRunningInDotNetFourPointFive ? new[] { mailWriter, true, true } : new[] { mailWriter, true }, null);
        result = new System.IO.MemoryStream(result.ToArray());
        CloseMethod.Invoke(mailWriter, Flags, null, new object[] { }, null);
        return result;
    }
}

So Robert Reid's example would become

        using (var mailMessage = new MailMessage("[email protected]", "[email protected]", "subject", "body"))
        {
            //Add an attachment just for the sake of it
            Attachment doc = new Attachment(@"filePath");
            doc.ContentId = "doc";
            mailMessage.Attachments.Add(doc);

            var smtpClient = new SmtpClient("SmtpHost")
            {
                EnableSsl = false,
                DeliveryMethod = SmtpDeliveryMethod.Network
            };

            // Apply credentials
            smtpClient.Credentials = new NetworkCredential("smtpUsername", "smtpPassword");

            // Send
            smtpClient.SendAndSaveMessageToIMAP(mailMessage, "imap.mail.com", 993, "imapUsername", "imapPassword", "SENT");
        }
Hysell answered 8/4, 2016 at 1:6 Comment(0)
E
1

I got same problem with SMTP server while using MailKit,

It was required to save email before sending:

private static async Task SaveMessageAsync(MimeMessage message, CancellationToken cancellationToken)
{
    using var client = new ImapClient();
    await client.ConnectAsync("host-address-of-server", 143, SecureSocketOptions.Auto, cancellationToken);
    await client.AuthenticateAsync("[email protected]", "somePassword", cancellationToken);

    var sentFolder = client.GetFolder(SpecialFolder.Sent);
    if (sentFolder == null)
    {
        var personalFolder = client.GetFolder(client.PersonalNamespaces[0]);
        sentFolder = await personalFolder.GetSubfolderAsync(nameof(SpecialFolder.Sent), cancellationToken);
    }

    await sentFolder.OpenAsync(FolderAccess.ReadWrite, cancellationToken);
    await sentFolder.AppendAsync(message, MessageFlags.Seen, cancellationToken);

    await client.DisconnectAsync(true, cancellationToken);
}

And the use it something like:

private async Task SendMessageAsync(MimeMessage message, CancellationToken cancellationToken)
{
    await SaveMessageAsync(message, cancellationToken);

    using var client = new SmtpClient();
    await client.ConnectAsync("host-address-of-server", 25, SecureSocketOptions.Auto, cancellationToken);
    await client.AuthenticateAsync("[email protected]", "somePassword", cancellationToken);

    await client.SendAsync(message, cancellationToken);
    await client.DisconnectAsync(true, cancellationToken);
}

P.S. I used some code from code from answers:

Enceladus answered 13/12, 2023 at 14:32 Comment(0)
A
0

You need to send the message from Outlook if you want to have the sent message in the "Sent messages" folder. This folder is an Outlook (and many other mail clients) concept, not an SMTP concept.

You can use the Outlook Automation API to ask Outlook to create an e-mail and send it.

Attrition answered 18/3, 2010 at 15:53 Comment(1)
+1 for "not an SMTP concept". This is actually the crux of the problem (though sending from Outlook is unlikely to work for the OP).Sticker

© 2022 - 2024 — McMap. All rights reserved.