I have a word document and using Aspose.Word to perform a mail merge and save the result to a memory stream as mhtml (part of my code):
Aspose.Words.Document doc = new Aspose.Words.Document(documentDirectory + countryLetterName);
doc.MailMerge.Execute(tempTable2);
MemoryStream outStream = new MemoryStream();
doc.Save(outStream, Aspose.Words.SaveFormat.Mhtml);
Then I use MimeKit (latest version from NuGet) to send my message:
outStream.Position = 0;
MimeMessage messageMimeKit = MimeMessage.Load(outStream);
messageMimeKit.From.Add(new MailboxAddress("<sender name>", "<sender email"));
messageMimeKit.To.Add(new MailboxAddress("<recipient name>", "<recipient email>"));
messageMimeKit.Subject = "my subject";
using (var client = new MailKit.Net.Smtp.SmtpClient())
{
client.Connect(<smtp server>, <smtp port>, true);
client.Authenticate("xxxx", "pwd");
client.Send(messageMimeKit);
client.Disconnect(true);
}
When opening the received email in my mail web client, I see the text (with image) and the image as attachment.
When opening the received email in Outlook (2016), the mail body is empty and I have two attachments, 1 with the text and 1 with the image.
Looking at the mht contents itself, it looks like:
MIME-Version: 1.0
Content-Type: multipart/related;
type="text/html";
boundary="=boundary.Aspose.Words=--"
This is a multi-part message in MIME format.
--=boundary.Aspose.Words=--
Content-Disposition: inline;
filename="document.html"
Content-Type: text/html;
charset="utf-8"
Content-Transfer-Encoding: quoted-printable
Content-Location: document.html
<html><head><meta http-equiv=3D"Content-Type" content=3D"text/html; charset=
=3Dutf-8" /><meta http-equiv=3D"Content-Style-Type" content=3D"text/css" />=
<meta name=3D"generator" content=3D"Aspose.Words for .NET 14.1.0.0" /><titl=
e></title></head><body>
*****body removed *****
</body></html>
--=boundary.Aspose.Words=--
Content-Disposition: inline;
filename="image.001.jpeg"
Content-Type: image/jpeg
Content-Transfer-Encoding: base64
Content-Location: image.001.jpeg
****image content remove****
--=boundary.Aspose.Words=----
Is there some formatting or so I have to do to get this correctly shown in Outlook? Or is it caused by the "3D"-keywords found, like content=3D"xxxx", style=3D"xxxx"?
Thanks in advance.
Edward
The =3D bits are the quoted-printable encoding of the = character. Since the headers properly declare the Content-Transfer-Encoding to be quoted-printable, that's not the problem.
Here are some suggestions on trying to massage the content into something that will work in Outlook (Outlook is very finicky):
MimeMessage messageMimeKit = MimeMessage.Load(outStream);
messageMimeKit.From.Add(new MailboxAddress("<sender name>", "<sender email"));
messageMimeKit.To.Add(new MailboxAddress("<recipient name>", "<recipient email>"));
messageMimeKit.Subject = "my subject";
var related = (MultipartRelated) messageMimeKit.Body;
var body = (MimePart) related[0];
// It's possible that the filename on the HTML body is confusing Outlook.
body.FileName = null;
// It's also possible that the Content-Location is confusing Outlook
body.ContentLocation = null;
Related
I am trying to use the Gmail API to send a HTML email from C#. The email gets sent but Gmail refuses to acknowledge that it should be an HTML email.
This is the code I am using:
var template = #"from: {1}{4}to: {0}{4}subject: {2}{4}MIME-Version: 1.0{4}Content-Type: text/html; charset=UTF-8{4}Content-Transfer-Encoding: base64{4}{4}{3}";
body = HttpUtility.HtmlEncode(body);
var result = string.Format(template, to, from, subject, body, "\r\n");
result = Convert.ToBase64String(Encoding.UTF8.GetBytes(result));
var gMessage = new Message()
{
Raw = result
};
service.Users.Messages.Send(gMessage, "me").Execute();
This is what the result string looks like before encoding to base64:
from: test#test.com
to: test#test2.com
subject: testSubject
MIME-Version: 1.0
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: base64
<html><head>
<title>Push Email</title>
</head>
<body> blah
</body></html>
(The app actually uses real email addresses which I have replaced with "test#..." in the above example for privacy.)
I tried every possible combination of header arrangement, content-transfer-encoding (base64, 7bit, 8bit, etc), content-type charset (ascii, utf8, etc), I tried using UrlEncode instead of HtmlEncode but the email body is either just displayed as unrendered HTML or it is displayed as an encoded url string (depending on whether I use html encode or url encode and what contet transfer encoding I specify).
The point is, the mail is working, the body is being sent but it just stubbornly refuses to render the HTML. I either get this:
<html><head> <title>Push Email</title> </head> <body> blah </body></html>
Or this:
%3chtml%3e%3chead%3e%0d%0a%0d%0a%3ctitle%3ePush+Email%3c%2ftitle%3e+++%0d%0a+%0d%0a%3c%2fhead%3e++++%0d%0a%3cbody%3e+blah++%0d%0a++++%0d%0a%3c%2fbody%3e%3c%2fhtml%3e
Or this:
<html><head> <title>Push Email</title> </head> <body> blah </body></html>
I would just send an SMTP email but, probably for security, Google won't allow it if you have 2 factor auth for the account (which I have and don't plan on disabling).
Also, I am just building my MIME message as a regular string. This may have something to do with it but I don't know. I don't plan on using any third party nuget packages / libraries such as MimeKit. I just want a C# solution only.
Finally, I need to be able to send HTML emails so that I may send links as per my app business logic.
Any advice?
I finally got it. First of all, the body of the mail must not be escaped but the whole MIME string should. But, as mentioned previously, if I leave the body unencoded the API complains about an invalid byte string.
The problem is that the resulting base64 string should be encoded in a URL safe manner. The python code on the Gmail API guide uses a method called urlsafe_b64encode which is different from the normal base 64 method in that the resulting string is URL safe.
I thought I could replicate this in C# using HTML or URL encoding and then using the standard Convert.ToBase64String method to convert the MIME string to base64 but I was wrong. After searching the MSDN website I finally found the HttpServerUtility.UrlTokenEncode method which does just what the urlsafe_b64encode python method does which is to encode the string in a URL safe variant and also convert it to base64. The final code thus becomes:
// Template for the MIME message string (with text/html content type)
var template = #"from: {1}{4}to: {0}{4}subject: {2}{4}MIME-Version: 1.0{4}Content-Type: text/html; charset=UTF-8{4}Content-Transfer-Encoding: base64{4}{4}{3}";
// Fill in MIME message fields
var result = string.Format(template, to, from, subject, body, "\r\n");
// Get the bytes from the string and convert it to a URL safe base64 string
result = HttpServerUtility.UrlTokenEncode(Encoding.UTF8.GetBytes(result));
// Instantiate a Gmail API message and assign it the encoded MIME message
var gMessage = new Message()
{
Raw = result
};
// Use the Gmail API Service to send the email
service.Users.Messages.Send(gMessage, "me").Execute();
Your body looks like passed by HtmlEncode twice, i.e. < once passed become < twice passed become < which is what you have. As you didn't posted the original code from where body is being setted before the enconding, all I can say is you have to have this as your result string before enconding to base64:
from: test#test.com
to: test#test2.com
subject: testSubject
MIME-Version: 1.0
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: base64
<html><head>
<title>Push Email</title>
</head>
<body> blah
</body></html>
To get this, you can replace this line
body = HttpUtility.HtmlEncode(body);
by this
body = HttpUtility.HtmlDecode(body);
Really just need the option "IsBodyHtml=true":
using System.Net;
using System.Net.Mail;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
var html = "<html><head><body><h1>hello world!</h1></body><head></html>";
var from = "me#here.com";
var to = "them#there.com";
var msg = new MailMessage(from, to)
{
Subject = "My Subject",
Body = html,
IsBodyHtml = true
};
SendGmail("myUserName", "myPassword", msg);
}
public static void SendGmail(string username, string password, MailMessage msg)
{
var client = new SmtpClient("smtp.gmail.com", 587);
client.EnableSsl = true;
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.Credentials = new NetworkCredential(username, password);
client.Send(msg);
}
}
}
thanks to #user1969903 give the direction, but i'm using .Net6, this work for me
// Template for the MIME message string (with text/html content type)
var mimeString = $"from: {from#gmail.com}\r\n" +
$"to: {to#gmail.com}\r\n" +
$"subject: Test Edm\r\n" +
$"MIME-Version: 1.0\r\n" +
$"Content-Type: text/html; charset=UTF-8\r\n" +
$"Content-Transfer-Encoding: base64\r\n\r\n" +
$"{html_here}";
var inputBytes = Encoding.UTF8.GetBytes(mimeString);
var gMessage = new Google.Apis.Gmail.v1.Data.Message()
{
Raw = Convert.ToBase64String(inputBytes).Replace("+", "-").Replace("/", "_").Replace("=", "")
};
service.Users.Messages.Send(gMessage, "me").Execute();
I'm still unable to extract the MIME attachment. Please check below MIME message. which we received from the service.
--MIMEBoundary_199ca6b7114b9acca5deb2047d25d5841d4afb7f68281379
Content-Type: application/xop+xml; charset=utf-8; type="text/xml"
Content-Transfer-Encoding: binary
Content-ID: <0.099ca6b7114b9acca5deb2047d25d5841d4afb7f68281379#apache.org>
<?xml version="1.0" encoding="utf-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Header><StateHeader xmlns="http://www.statemef.com/StateGatewayService"><MessageID>12345201704200009962</MessageID><RelatesTo>12345201704200009962</RelatesTo><Action>GetNewAcks</Action><Timestamp>2017-02-11T01:54:51.676-05:00</Timestamp><TestIndicator>T</TestIndicator></StateHeader></soapenv:Header><soapenv:Body><GetNewAcksResponse xmlns="http://www.statemef.com/StateGatewayService"><MoreAvailable>true</MoreAvailable><AcknowledgementListAttachmentMTOM><xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:299ca6b7114b9acca5deb2047d25d5841d4afb7f68281379#apache.org"></xop:Include></AcknowledgementListAttachmentMTOM></GetNewAcksResponse></soapenv:Body></soapenv:Envelope>
--MIMEBoundary_199ca6b7114b9acca5deb2047d25d5841d4afb7f68281379
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
Content-ID: <299ca6b7114b9acca5deb2047d25d5841d4afb7f68281379#apache.org>
Step 1: Get the complete MIME stream, i.e. the Content-Type header that defines the boundary parameter to be MIMEBoundary_199ca6b7114b9acca5deb2047d25d5841d4afb7f68281379. Without that, you are SOL.
If you are using something like HttpWebRequest, proceed to Step 2.
Step 2: Follow the instructions in the MimeKit FAQ:
How would I parse multipart/form-data from an HTTP web request?
Since classes like HttpWebResponse take care of parsing the HTTP headers (which includes the Content-Type header) and only offer a content stream to consume, MimeKit provides a way to deal with this using the following
two static methods on MimeEntity:
public static MimeEntity Load (ParserOptions options, ContentType contentType, Stream content, CancellationToken cancellationToken = default (CancellationToken));
public static MimeEntity Load (ContentType contentType, Stream content, CancellationToken cancellationToken = default (CancellationToken));
Here's how you might use these methods:
MimeEntity ParseMultipartFormData (HttpWebResponse response)
{
var contentType = ContentType.Parse (response.ContentType);
return MimeEntity.Load (contentType, response.GetResponseStream ());
}
Once you have the MimeEntity, you can cast it to a Multipart and enumerate the attachments within, saving the content to a stream like this:
int i = 1;
foreach (var attachment in multipart.OfType<MimePart> ()) {
string fileName = string.Format ("attachment.{0}.dat", i++);
using (var stream = File.Create (fileName))
attachment.ContentObject.DecodeTo (stream);
}
The question shows only the response Body. To parse it, you should prepend the response Header.
For example, it should look like:
MIME-Version: 1.0
content-type: multipart/related; type="application/xop+xml";start="<http://tempuri.org/0>";boundary="MIMEBoundary_someuniqueID";start-info="text/xml"
Server: Microsoft-IIS/10.0
X-Powered-By: ASP.NET
Content-Length:24371900
--MIMEBoundary_someuniqueID
Content-Type: application/xop+xml; charset=utf-8; type="text/xml" Content-Transfer-Encoding: binary
Content-ID: <http://tempuri.org/0>
<soap:Envelope>
<someWrapperElt>
<xop:Include href="cid:uri_of_content"></xop:Include>
</someWrapperElt>
</soap:Envelope>
--MIMEBoundary_someuniqueID
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
Content-ID: <uri_of_content>
...start.b1n#ry-content-here-etc.fckZ8990832d...
--MIMEBoundary_someuniqueID--
Then convert the whole response into a MemoryStream object, and use XmlDictionaryReader to parse it.
XmlDictionaryReader mtomReader = XmlDictionaryReader.CreateMtomReader(ms, Encoding.UTF8, XmlDictionaryReaderQuotas.Max);
That is, you can now extract desired values out of the mtomReader object, including the attachment.
You can take parser project in Github
WebResponseDerializer component can parse multipart soap message
1) Copy xml message between soap body tag to xml2charp site and take deserilized object.
2) Take response stream and call like below.
Byte[] file = File.ReadAllBytes("..\\..\\Data\\ccc.xxx");
Stream stream = new MemoryStream(file);
WebResponseDerializer<SIGetImageResponse> deserilizer = new WebResponseDerializer<SIGetImageResponse>(stream);
SIGetImageResponse ddd = deserilizer.GetData();
foreach (var item in ddd.ResponseData.AttachmentDescriptor.Attachment)
{
String contentId = "<<" + item.ImageData.Include.Href + ">>";
contentId = contentId.Replace("%40", "#").Replace("cid:", "");
item.ImageData.Include.XopData = deserilizer.GetAttachment(contentId);
}
I have a database table of email called email_archive. The table includes a field called body and another called raw_headers. I want to display the contents of this table on screen using C# (in a SharePoint webpart). I've been trying to find a library that can parse the body so that I can return the parts of the message to the window. I've tried a library from Limilabs as well as downloaded a couple of other libraries. However, all appear to require an email in EML format at the very minimal.
The latest attempt was trying to use MailUtilies.
MimeMessage mm = new MimeMessage(header + message);
But this is failing because it appears the format is not passing the MimeMessage integrity check.
Does anyone know of a way to parse an email into it's component parts using the raw headers and body content.
The headers look like this
MIME-Version: 1.0
Received: from server.domain.com (10.20.205.104) by
mail.domain.com (xx.xx.xx.xx) with Microsoft SMTP Server id
8.1.436.0; Mon, 16 Sep 2013 14:33:54 -0700
Received: from server (localhost.localdomain [127.0.0.1]) by
server.domain.com (8.13.8/8.13.8) with ESMTP id r8GLX4vm007046 for
<myaddress#domain.com>; Mon, 16 Sep 2013 14:33:04 -0700
From: "service#domain.com" <service#domain.com>
To: My Name <myaddress#domain.com>
Date: Mon, 16 Sep 2013 14:33:04 -0700
Subject: Some Topic
Thread-Topic: Some Topic
Thread-Index: Ac6zJHFgOvb7ZAdeTJC8DzqnAvdnOw==
Message-ID: <153372.442207427-sendEmail#gserver>
Reply-To: "service#domain.com" <service#domain.com>
Accept-Language: en-US
Content-Language: en-US
X-MS-Exchange-Organization-AuthAs: Internal
X-MS-Exchange-Organization-AuthMechanism: 10
X-MS-Exchange-Organization-AuthSource: mail.domain.com
X-MS-Has-Attach:
X-MS-TNEF-Correlator:
Content-Type: multipart/alternative;
boundary="_000_153372442207427sendEmailgroundwork_"
And the message looks something like this
--_000_153372442207427sendEmailgroundwork_
Content-Type: text/plain; charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
Some message to dispaly
--_000_153372442207427sendEmailgroundwork_
Content-Type: text/html; charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
<html><head><style type=3D'text/css'> p,h1 { font-family: arial; }
</style></head><body>
<p>Some message to display</p>
</body></html>
--_000_153372442207427sendEmailgroundwork_--
I found the answer by using a library using OpenPop.Net.
public void addMessage(string message, string header) {
string full_body = header + "\n" + message;
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
Byte[] full_body_bytes = encoding.GetBytes(full_body);
Message mm = new Message(full_body_bytes);
//do stuff here.
}
I just used MimeKit and it worked great for parsing out attachments. It appears to be fully featured and cross-platform.
You need to combine your header and body with the \r\n\r\n delimiter. The following code demonstrates that:
string msgContent = header.TrimEnd("\r\n") + "\r\n\r\n" + message;
byte[] bytes = Encoding.ASCII.GetBytes(msgContent);
ComponentPro.Net.Mail.MailMessage msg = new ComponentPro.Net.Mail.MailMessage(bytes);
//
// Access your parsed message here
//
The code uses Ultimate Mail library
If you have headers and body, it's trivial to re-create entire eml:
string eml = header + "\r\n\r\n" + body;
Headers and body in MIME format are separated by one empty line.
The other answer uses '\n' - which is incorrect as MIME requires "\r\n" sequence as line ending.
If your header string already ends with new line, you just need to add a single new line ("\r\n").
I'm using C#, .NET 4.0 to send a digitally-signed email, like so:
private void SendMailMessage(string emailTo)
{
MailMessage message = new MailMessage();
message.From = new MailAddress(fromAddress);
message.To.Add(new MailAddress(emailTo));
message.Subject = "Regarding your lottery winnings";
message.IsBodyHtml = false;
string body = "Content-Type: text/plain;charset=\"iso-8859-1\"\nContent-Transfer-Encoding: quoted-printable\n\nThe URL to your secret is: " + url + "\nIt can only be viewed once.";
byte[] messageBytes = Encoding.ASCII.GetBytes(body);
ContentInfo content = new ContentInfo(messageBytes);
SignedCms signedCms = new SignedCms(content, false);
CmsSigner Signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, emailCert);
signedCms.ComputeSignature(Signer);
byte[] signedBytes = signedCms.Encode();
MemoryStream ms = new MemoryStream(signedBytes);
AlternateView av = new AlternateView(ms, "application/pkcs7-mime; smime-type=signed-data;name=smime.p7m");
message.AlternateViews.Add(av);
SmtpClient client = new SmtpClient(smtpServer, int.Parse(smtpServerPort));
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.Send(message);
message.Dispose();
client = null;
}
Notice that message.Body is left blank, and only the AlternateView is added to the email. When I send this email and view it in Outlook, it shows up perfectly, with a certificate icon on the email message, and the S/MIME Outlook extensions validate the signature successfully and automatically.
Aces.
(If I add anything to message.Body, it breaks. Outlook no longer recognizes it as a signed email and I only see the message.Body text, not the AlternateView.)
But if I send this email to a Gmail address, for instance, it shows up as a blank email with smime.p7m as an attachment, and inside it I see the text of the email, but it's surrounded by what looks like a ton of binary gibberish.
Is there a way to make this digitally signed email compatible with both an Outlook client and a Gmail web client?
When outlook generates a signed email it adds an alternate view with the signed message, another alternate view with the html version and then another alternate view with a plain text version. I think if you also do this then it will work in most all email clients.
there is a plain text alternate view that is not signed
Content-Type: text/plain;
charset="us-ascii"
Content-Transfer-Encoding: 7bit
there is an html version that is not signed
Content-Type: text/html;
charset="us-ascii"
Content-Transfer-Encoding: quoted-printable
There is a signed alternate view
Content-Type: application/pkcs7-signature;
name="smime.p7s"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="smime.p7s"
Can anyone recreate this issue? It seems to me like a quite serious bug in SmtpClient (.NET 4.0) but I can't believe no one has seen this before and Google doesn't seem to show anyone seeing a similar issue.
When sending an email with more than 1 attachment and the 'Attachment.Name' property is used, the attachments will have the incorrect names (e.g. 2 attachments will have their names swapped). The work around (and actually probably the correct property to set) is to use ContentDisposition.FileName. But I would be very interested if this happens for everyone. Can anyone recreate this issue? It seems to me like a quite serious bug in SmtpClient (.NET 4.0) but I can't believe no one has seen this before and Google doesn't seem to show anyone seeing a similar issue. You'll need to create a couple of zip files in c:\tmp\emailin\
var zipCt = new ContentType { MediaType = MediaTypeNames.Application.Zip };
var attachmentA = new Attachment(#"c:\tmp\emailin\a.zip", zipCt);
attachmentA.ContentDisposition.FileName = "a.zip";
attachmentA.Name = "a.zip";
var attachmentB = new Attachment(#"c:\tmp\emailin\b.zip", zipCt);
attachmentB.ContentDisposition.FileName = "b.zip";
attachmentB.Name = "b.zip";
var msg = new MailMessage("testfrom#example.com", "testto#example.com")
{
Body = "body",
Subject = "subject"
};
msg.Attachments.Add(attachmentA);
msg.Attachments.Add(attachmentB);
using (var smtp = new SmtpClient())
{
smtp.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory;
smtp.PickupDirectoryLocation = #"c:\tmp\emailout\";
smtp.Send(msg);
}
If you now look at the eml file in c:\tmp\emailout\ you will see something like
X-Sender: testfrom#example.com
X-Receiver: testto#example.com
MIME-Version: 1.0
From: testfrom#example.com
To: testto#example.com
Date: 11 Apr 2012 12:36:48 +0100
Subject: subject
Content-Type: multipart/mixed; boundary=--boundary_0_1b7bb1ee-ba28-4258-b662-554adb7ff81a
----boundary_0_1b7bb1ee-ba28-4258-b662-554adb7ff81a
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: quoted-printable
body
----boundary_0_1b7bb1ee-ba28-4258-b662-554adb7ff81a
Content-Type: application/zip; name=b.zip
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=a.zip
UEsDBAoAAAAAAG5ki0AAAAAAAAAAAAAAAAAFAAAAYS50eHRQSwECPwAKAAAAAABu
ZItAAAAAAAAAAAAAAAAABQAkAAAAAAAAACAAAAAAAAAAYS50eHQKACAAAAAAAAEA
GADa2JQw1xfNAdrYlDDXF80B2tiUMNcXzQFQSwUGAAAAAAEAAQBXAAAAIwAAAAAA
----boundary_0_1b7bb1ee-ba28-4258-b662-554adb7ff81a
Content-Type: application/zip; name=a.zip
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=b.zip
UEsDBAoAAAAAAHZki0AAAAAAAAAAAAAAAAAFAAAAYi50eHRQSwECPwAKAAAAAAB2
ZItAAAAAAAAAAAAAAAAABQAkAAAAAAAAACAAAAAAAAAAYi50eHQKACAAAAAAAAEA
GAD67/k51xfNAfrv+TnXF80B2tiUMNcXzQFQSwUGAAAAAAEAAQBXAAAAIwAAAAAA
----boundary_0_1b7bb1ee-ba28-4258-b662-554adb7ff81a--
Note how the Content-Type: and Content-Disposition: file names do not match for each attachment.
Am I doing something wrong? Is this a bug that I should log with MS?
This is because you require a new instance of ContentType for each attachment.
var zipCt = new ContentType { MediaType = MediaTypeNames.Application.Zip };
var zipCt2 = new ContentType { MediaType = MediaTypeNames.Application.Zip };
var attachmentA = new Attachment(#"c:\tmp\emailin\a.zip", zipCt);
attachmentA.ContentDisposition.FileName = "a.zip";
attachmentA.Name = "a.zip";
var attachmentB = new Attachment(#"c:\tmp\emailin\b.zip", zipCt2);
attachmentB.ContentDisposition.FileName = "b.zip";
attachmentB.Name = "b.zip";
Should fix your issue.