System.Net.Mail alternative without message size increase - email

We are using the System.Net.Mail to send email messages as text with attachments. The attachments are Excel and Powerpoint files. The content types are set to the MIME types before sending the email.
A test done with three emails proved that the Exchange server recorded a 26% increase in each of the cases.
Is there a way to stop this increase in message size?
If not, is there a another .NET or open source alternative for this?
Would SMTP Drop overcome this issue?
UPDATE:
var mimeMessage = new MimeMessage();
mimeMessage.From.Add(new MailboxAddress(string.Empty, fromEmailAddress));
mimeMessage.To.Add(new MailboxAddress(string.Empty, toEmailAddress));
mimeMessage.Cc.Add(new MailboxAddress(string.Empty, copyEmailAddress));
mimeMessage.Subject = subject;
var builder = new BodyBuilder { HtmlBody = bodyText };
MvcApplication.Logger.Info("SendEmail:attachmentFiles:Count=" + attachmentFiles.Count);
foreach (var attachmentFile in attachmentFiles)
{
var attachment = new MimePart()
{
ContentObject = new ContentObject(File.OpenRead(attachmentFile)),
ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
ContentTransferEncoding = ContentEncoding.Binary,
FileName = Path.GetFileName(attachmentFile)
};
builder.Attachments.Add(attachment);
}
MvcApplication.Logger.Info("SendEmail:attachmentFiles:Added Count=" + builder.Attachments.Count);
mimeMessage.Body = builder.ToMessageBody();
using (var client = new SmtpClient())
{
client.Connect("smtp.domain.com", 25, false);
client.Send(mimeMessage);
MvcApplication.Logger.Info(
client.Capabilities.HasFlag(SmtpCapabilities.BinaryMime)
? "SMTP Server supports BinaryMime"
: "SMTP Server does NOT support BinaryMime");
client.Disconnect(true);
}
The above code sends a HTML message successfully.
The SMTP Server Capabilities flag for BinaryMime returns true.
If the ContentTransferEncoding is Base64 it works(9 excel and powerpoint files attached). If I change it Binary then just one corrupt excel file is attached. What am I missing here?

If the SMTP server supports the BINARYMIME extension, you could use MailKit to send email and set the Content-Transfer-Encoding of the attachments to ContentEncoding.Binary.
I don't think that System.Net.Mail supports the BINARYMIMNE SMTP extension (at least it didn't look like it did when I reviewed the referencesource on GitHub), hence System.Net.Mail.SmtpClient will always base64 or quoted-printable encode attachments.
The BINARYMIME SMTP extension allows clients to send messages without the need to abse64 or quoted-printable encode them.

Related

Mimekit / MailKit BSMTP Files

Try to automate a process here - we have a system that generated batch SMTP files dumps them to a folder - a 3rd party .net component reads the file and then sends it along to our SMTP server. Problem is this component create and destroys a new smtp connection with every email. So painfully slow - I checked with the vendor and no plans to do it any other way ..ever.. So wanted to spin up my own simple service - the feeding application creates batch smtp files.. I try to load this via MimeKit and get an parsing error - which i get not really a normal MIME a client would use.. is there any option to get MimeKit or even MailKit to load and relay this mail?
Simplified format of the file.. all the data is muged up for clarity - I guess I could strip out the smtp command and set them in the code and just stream in the actual MIM portion - but seems there should be a simpler way
PORT 25
HELO mydomain.com
MAIL FROM: <asdlfjasdfasdfadsf#mydomain.com>
RCPT TO: <foobar#blah.com>
DATA
From: <foobar#foo.com>
To: foovar#blah.com
Subject: Test
Date: Wed, 04 Nov 2020 11:31:52 -0600
Message-ID: <2asdfadfadaf9-0#v116.mydomain.com>
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary="--=036c8f0f_0fd5_48a2_85b7_b5310c3b811b"
----=036c8f0f_0fd5_48a2_85b7_b5310c3b811b
Content-Type: multipart/alternative;
boundary="--=6b227ff4_d132_4000_9af6_f92a291f8101"
----=6b227ff4_d132_4000_9af6_f92a291f8101
Content-Type: text/plain;charset="UTF-8"
Content-Transfer-Encoding: Quoted-Printable
some content here
----=6b227ff4_d132_4000_9af6_f92a291f8101
Content-Type: text/html;charset="UTF-8"
Content-Transfer-Encoding: Quoted-Printable
html content here
----=6b227ff4_d132_4000_9af6_f92a291f8101--
----=036c8f0f_0fd5_48a2_85b7_b5310c3b811b--
.
QUIT
I guess I could strip out the smtp command and set them in the code and just stream in the actual MIM portion
That's what you'll have to do. There's no other way to do it. Keep in mind that you'll also want to strip out the line that is just . and the QUIT line as well.
The file format you've got is essentially an "SMTP command script" which includes embedded MIME, but isn't MIME itself and so MimeKit/MailKit's parser cannot handle it.
There's no real need to keep the SMTP commands that you strip out because MailKit's SmtpClient doesn't allow you to send commands since it handles sending them itself.
MailKit auto-detects the addresses it needs to send from/to based on the Sender/From and To/Cc/Bcc headers, but I suppose that it might be worth parsing the MAILFROM: and RCPT TO: commands in order to get an explicit list.
Something like this:
var recipients = new List<MailboxAddress> ();
MailboxAddress sender = null;
if (line.StartsWith ("MAIL FROM:", StringComparison.OrdinalIgnoreCase)) {
var address = line.Substring ("MAIL FROM:".Length);
if (MailboxAddress.TryParse (address, out var mailbox))
sender = mailbox;
} else if (line.StartsWith ("RCPT TO:", StringComparison.OrdinalIgnoreCase)) {
var address = line.Substring ("RCPT TO:".Length);
if (MailboxAddress.TryParse (address, out var mailbox))
recipients.Add (mailbox);
}
The hardest part will be to process the batch smtp files efficiently. You could use a StreamReader to read line-by-line, processing input until you've read a DATA line (everything following that is part of the message right up until you encounter a line matching .), but the problem with that is then you'd need to continue using the StreamReader to read data and dump it into a MemoryStream or something that you could then parse using MimeMessage.Load() (PS: don't forget to rewind the stream before parsing it).
A better option if you have the skills to do it would be to implement a custom IMimeFilter and then use MimeMessage.Load() with a FilteredStream that filters the source stream through your custom filter.
For some sample filter implementations, take a look at https://github.com/jstedfast/MimeKit/tree/master/MimeKit/IO/Filters
The easiest ones to understand might be the Unix2Dos and Dos2Unix filters.
Another thing to keep in mind is that as you are filtering the "MIME" portion of the content in those batch files, you will need to unmunge lines that start with two dots (..) by removing the first dot (.).
The Pop3Stream code in MailKit has to do this process as well since POP3 munges MIME lines beginning with a . by prepending an extra ..
You can see that code here: https://github.com/jstedfast/MailKit/blob/master/MailKit/Net/Pop3/Pop3Stream.cs#L344
(Unfortunately, it is pretty complex logic because it deals with buffering as well).
Then, once you've stripped out all of the lines with SMTP commands and processed them with the above code snippet, you can send via MailKit's SmtpClient using the following logic:
using (var client = new SmtpClient ()) {
client.Connect (host, port, SecureSocketOptions.Auto);
client.Authenticate (username, password);
foreach (batchFile in batchFiles) {
using (var stream = File.OpenRead (batchFile)) {
var message = MyCustomParseMessage (stream, out MailbnoxAddress sender, out List<MailboxAddress> recipients);
client.Send (message, sender, recipients);
}
}
client.Disconnect (true);
}

Modifying the content type of an attachment in a CDO.Message object

When I try to add an MHTML file as an attachment to an email message in VBScript, the ContentMediaType is incorrectly set to "message/rfc822" (RFC 822). From what I understand, this is correct according to Microsoft, but is incorrect according to RFC 2557 which states that it should be "multipart/related". This is a problem, because most (if not all) mail clients interpret "message/rfc822" as an email message. Since the file extensions ".mht" and ".mhtml" do not match any valid file extension of an email message, the mail client appends one of ".msg", ".eml", etc. to the filename. When a user opens the attachment, it opens as an email message and doesn't display correctly since an MHTML file and an email message are saved differently.
Sub SendEmail(FromAddress, ToAddress, Subject, Body, Attachment)
Call Err.Clear
On Error Resume Next
Schema = "http://schemas.microsoft.com/cdo/configuration/"
Set Configuration = Sys.OleObject("CDO.Configuration")
Configuration.Fields.Item(Schema + "sendusing") = 2
Configuration.Fields.Item(Schema + "smtpserver") = SMTPServer
Configuration.Fields.Item(Schema + "smtpserverport") = 25
Configuration.Fields.Item(Schema + "smtpauthenticate") = 1
' Configuration.Fields.Item(schema + "sendusername") = ""
' Configuration.Fields.Item(schema + "sendpassword") = ""
Call Configuration.Fields.Update
Set Message = Sys.OleObject("CDO.Message")
Set Message.Configuration = Configuration
Message.From = FromAddress
Message.To = ToAddress
Message.Subject = Subject
Message.HTMLBody = Body
If Not IsEmpty(Attachment) Then
'CDO.Message.AddAttachment doesn't set the correct content media type for an MHTML file.
Call Message.AddAttachment(Attachment)
End If
Call Message.Send
End Sub
When I run this code, Message.Attachments.Item(1).ContentMediaType is set to "message/rfc822". I need it to be "multipart/related" if Attachment (a string) ends with ".mht" or ".mhtml" (case-insensitive). I can do this with the following code.
If Len(Attachment) >= 4 And InStr(Len(Attachment) - 3, Attachment, ".mht", vbTextCompare) Or Len(Attachment) >= 4 And InStr(Len(Attachment) - 5, Attachment, ".mhtml", vbTextCompare) Then
Message.Attachments.Item(1).ContentMediaType = "multipart/related"
End If
For some unknown reason, this undefines the attachment from Message.Attachments.
I've looked at manually adding the attachment per these instructions, but when I call Message.Attachments.Item(1).Fields.Update, the object becomes undefined. I think setting the attachments's ContentMediaType, implicitly invokes it's Fields's Update method which is what I think is responsible for this unexpected behavior.
How can I get around this and send an MHTML file with the "multipart/related" content type while maintaining the proper file extension?
So your problem is that at least some email clients save MHTML attachment incorrectly if content type for the attachment is set as content-type="message/rfc822".
First, it is worth noting that your analysis of a root cause of the issue is flawed. You appear to be confused by where multipart/related MIME type comes into play. As a matter of fact, RFC 2557 does NOT state that body part corresponding to MHTML attachment must have content-type="multipart/related". Instead, MIME multipart/related is internal structure of MHTML file itself. Quoting Wikipedia article:
The content of an MHTML file is encoded as if it were an HTML e-mail message, using the MIME type multipart/related.
I.e. if you open MHTML file with text editor, you should see the following:
Content-Type: multipart/related; ...
Microsoft states that MHTML files should be served with content-type="message/rfc822" in KB937912. This is exactly what CDO does by default when you attach such file via AddAttachment method. I believe such behavior does not contradict RFC 2557 in any way. As per the RFC:
There are a number of document formats... that specify
documents consisting of a root resource and a number of distinct
subsidiary resources referenced by URIs within that root resource.
There is an obvious need to be able to send such multi-resource
documents in e-mail [SMTP], [RFC822] messages.
The standard defined in this document specifies how to aggregate such
multi-resource documents in MIME-formatted [MIME1 to MIME5] messages
for precisely this purpose.
To recap, you definitely should not set content type of MHTML attachment to multipart/related.
While message/rfc822 seems to be the way to use with MHTML files, it obviously triggers the problem you described in the question. I tested with Outlook 2010 and OWA 2010, and was able to reproduce it.
Alternative content types that are used by various email clients for MHTML attachments are application/octet-stream and application/x-mimearchive. These two didn't exhibit the problem in my tests.

Sending email with attachment

I've a custom form (created with form API) that need send an uploaded file by email. The current form submit handler sends the email without attachment using drupal_mail().
So I'm looking for a solution to properly send email with attachment from Drupal. Mime Mail seems an overkill because HTML mail, templating and its other features are not required. But the only other alternative I see is to set the appropriate headers and serialize the attached file in the mail body when processing the mail in my hook_mail() implementation.
Did I miss anything? Is there any module to handle this?
Mimemail is the easiest solution here. Be it an overkill or not, it will allow you to get it done with a single function call.
If you insist, you may have your homemade attachment sender: base64 encode your attachment(s), add them to the mail body, add the correct headers and you're done.
You can use mime mail and force the message body to be sent in plaintext format. Here is an excerpt from the module's readme file:
USAGE
This module may be required by other modules, but is not terribly
useful by itself. Once installed, any module can send messages by
calling the mimemail() function:
$sender - a user object, text email address or an array with name, mail
$recipient - a user object, text email address or an array with name, mail
$subject - subject line
$body - body text in HTML format
$plaintext - boolean, whether to send messages in plaintext-only (default FALSE)
$headers - a keyed array with headers (optional)
$text - plaintext portion of a multipart e-mail (optional)
$attachments - array of arrays with the file's path, MIME type (optional)
$mailkey - message identifier
return - an array containing the MIME encoded message
The key thing being to set the $plaintext argument to TRUE. Now you can have your cake and eat it too.
You could always have a look at the Swift Mailer module which lets you send HTML (MIME) e-mails, e-mails with inline images and e-mails with attachments. It is also cabable of automatically generating plain text versions based on the HTML e-mail version, which in the end will let the user's e-mail client display the preferred version (HTML or plain text).
The Swift Mailer module is available on http://drupal.org/project/swiftmailer.
For the record : I'm the author and maintainer of the module.
The Webform module allows you to create a form and has a file option which can be used as an attachment. All available form components are listed on the module's manual page.
Once installed Webform will appear as a content type. Once you have saved the fundamentals, such as the title and the email to address, you will have the ability to add the required form components.
Add a component of type 'file', ensuring the 'email' (to recipient) option is ticked, and you will then be able to customize the permitted file types, extensions, sizes and upload folder.
You could use the Zend Framework.
function sendEmail($params){
ini_set('include_path', 'inc/');
require_once ('inc/Zend/Mail.php');
$mail = new Zend_Mail();
$mail->setSubject( $params['subject'] );
$mail->setBodyText( $params['bodyText'] );
$mail->setBodyHtml( $params['bodyHtml'] );
$mail->setFrom( $params['fromEmail'], $params['fromName'] );
$mail->addTo( $params['toEmail'], $params['toName'] );
// Finally, add an attachment
assert( file_exists($params['attachFile']) );
$at = $mail->addAttachment(file_get_contents($params['attachFile']));
$at->type = $params['attachType'];
$at->disposition = Zend_Mime::DISPOSITION_ATTACHMENT;
$at->filename = $params['attachName'];
$mail->send();
}

Using the Microsoft SMTP Server's Dropfolder

I have set up Microsoft SMTP server so it will store all incoming email in a dropfolder.
I want to process, using c#, incoming mail based on the sender, recipient, and subject line. If possible, I also want to create a plain text preview of the email.
So, there are two parts to this problem.
I'm guessing a FileSystemWatcher
would be adequate for providing
notification of incoming mail.
How to parse the headers and body text from the .eml file; is there an existing library or any good documentation on the format?
Thanks for any help.
Yes - thats true
I used this: http://www.lumisoft.ee/lswww/ENG/Products/Mail_Server/mail_index_eng.aspx?type=info
It's a Mailserver written in C# with an API you can use without using the Mailserver
EDIT: Found a code snippet:
LumiSoft.Net.Mime.Mime m = LumiSoft.Net.Mime.Mime.Parse(mailfile);
Console.WriteLine("Read message from: " + m.MainEntity.From);
Console.WriteLine("To: " + m.MainEntity.To[0]);

CDO message: text attachment gets corrupted

I'm creating an email message using CDO object (and VB6, but that doesn't really matter).
With New CDO.Message
.To = "<address>"
.Subject = "Manifest test 8"
.Organization = "<company>"
.From = "<address>"
.Sender = .From
With .Configuration
.Fields(cdoSendUsingMethod).Value = cdoSendUsingPort
.Fields(cdoSMTPServer).Value = "192.168.0.4"
.Fields.Update
End With
With .AddAttachment("c:\import\customermanifestOURACCOUNT11122008110032.dat")
.Fields(cdoContentDisposition).Value = "attachment; filename=""Consignor.dat"""
.Fields.Update
End With
.Send
End With
As you can see, the message is empty and contains an attachment that I rename in the email.
The attachment is an ASCII text file, fixed-width, that contains some output from our systems, one record per line, separated with CRLF.
When the message gets sent, all CRs get stripped out the attachment, so the receiver gets a file that only has LFs and therefore is corrupted.
I tried to change ContentEncoding to 7bit and base64, didn't work.
I tried to set ContentMediaType for the attachment to text/plain, didn't work.
I tried to not rename the attachment after adding, didn't work.
The ContentMediaType for the attachment is set to application/octet-stream by default, so I can't figure out why (and by what) it gets changed in the first place.
If I execute .SaveToFile() on the attachment right after .Send(), it saves valid file on the disk.
Is this a problem in my code, or is it a mail server setting or something?
Ok, that was weird.
We used our gmail accounts to test that thing, more specifically, gmail web interface. We clicked attachments links to save reveived files. And the files were corrupted.
As soon as we instead tried some thick clients, it turned out to be fine. All files get download properly without any corruptions.
So I assume this is a bug in gmail web interface (as opposed to gmail POP3 interface).
I have not used CDO in a long time, but i remember having this issue in the past. By trying different things, we figured that if there was any content in the body of the message the attachments were sent properly.
Weird, i know.
try it and let us know.
I just got this same trouble. I switched to Jmail and my CSV (text file) was now exactly like the original when I read it from gmail.
I compared the original message (in Gmail, option - view original message) and discovered that with CDO.Message, the attachment is not encoded really well, it is kept in text format, and the mail client do what he wants with it. With Jmail, the message is encoded in Base64 so it is kept in its original state from the start to the end. So, be careful when using CDO.Message with text file attachments! Try using Jmail (free) instead.
CDO.message: Content-Transfer-Encoding: quoted-printable
Jmail.message: Content-Transfer-Encoding: base64