MailMessage Attachment filename with accents - c#

I'm trying to send HTML e-mails with attached Excel filenames. It all worked well until I neded to send messages whose attachment name contains accented letters :-( Every workaround I've tried failed miserably.
Original code:
var attachment = new Attachment(
new MemoryStream(excelFileContents),
"simplefilename.xls");
This one works fine.
However if I replace "simplefilename.xls" by "échec.xls", the attachment is garbaged (name and contents).
I tried these, to no avail:
var attachment = new Attachment(
new MemoryStream(excelFileContents),
new System.Net.Mime.ContentType("application/vnd.ms-excel"));
attachment.Name = "échec.xls";
The last one is even worse: SmtpClient.Send() throws an exception, complaining about the é in the filename:
var attachment = new Attachment(
new MemoryStream(excelFileContents),
new System.Net.Mime.ContentType("application/vnd.ms-excel"));
attachment.ContentDisposition.FileName = "échec.xls";
I've been banging my head on this one for way too long. Any lights warmly welcomed!

I finally came across an answer that worked.
http://social.msdn.microsoft.com/Forums/en-US/dotnetframeworkde/thread/b6c764f7-4697-4394-b45f-128a24306d55
The content is in German except for the post by Marcel Roma. I put in his code into my application. I was able to send out pdf with accents and we were to see the attachment instead of garbage.
So here it is:
public class AttachmentHelper
{
public static System.Net.Mail.Attachment CreateAttachment(string attachmentFile, string displayName, TransferEncoding transferEncoding)
{
System.Net.Mail.Attachment attachment = new System.Net.Mail.Attachment(attachmentFile);
attachment.TransferEncoding = transferEncoding;
string tranferEncodingMarker = String.Empty;
string encodingMarker = String.Empty;
int maxChunkLength = 0;
switch (transferEncoding)
{
case TransferEncoding.Base64:
tranferEncodingMarker = "B";
encodingMarker = "UTF-8";
maxChunkLength = 30;
break;
case TransferEncoding.QuotedPrintable:
tranferEncodingMarker = "Q";
encodingMarker = "ISO-8859-1";
maxChunkLength = 76;
break;
default:
throw (new ArgumentException(String.Format("The specified TransferEncoding is not supported: {0}", transferEncoding, "transferEncoding")));
}
attachment.NameEncoding = Encoding.GetEncoding(encodingMarker);
string encodingtoken = String.Format("=?{0}?{1}?", encodingMarker, tranferEncodingMarker);
string softbreak = "?=";
string encodedAttachmentName = encodingtoken;
if (attachment.TransferEncoding == TransferEncoding.QuotedPrintable)
encodedAttachmentName = HttpUtility.UrlEncode(displayName, Encoding.Default).Replace("+", " ").Replace("%", "=");
else
encodedAttachmentName = System.Convert.ToBase64String(Encoding.UTF8.GetBytes(displayName));
encodedAttachmentName = SplitEncodedAttachmentName(encodingtoken, softbreak, maxChunkLength, encodedAttachmentName);
attachment.Name = encodedAttachmentName;
return attachment;
}
private static string SplitEncodedAttachmentName(string encodingtoken, string softbreak, int maxChunkLength, string encoded)
{
int splitLength = maxChunkLength - encodingtoken.Length - (softbreak.Length * 2);
var parts = SplitByLength(encoded, splitLength);
string encodedAttachmentName = encodingtoken;
foreach (var part in parts)
encodedAttachmentName += part + softbreak + encodingtoken;
encodedAttachmentName = encodedAttachmentName.Remove(encodedAttachmentName.Length - encodingtoken.Length, encodingtoken.Length);
return encodedAttachmentName;
}
private static IEnumerable<string> SplitByLength(string stringToSplit, int length)
{
while (stringToSplit.Length > length)
{
yield return stringToSplit.Substring(0, length);
stringToSplit = stringToSplit.Substring(length);
}
if (stringToSplit.Length > 0) yield return stringToSplit;
}
}
Use it in the following way:
static void Main(string[] args)
{
string smtpServer = String.Empty;
string userName = String.Empty;
string password = String.Empty;
string attachmentFilePath = String.Empty;
string displayName = String.Empty;
System.Net.Mail.SmtpClient client = new System.Net.Mail.SmtpClient(smtpServer);
client.Credentials = new System.Net.NetworkCredential(userName, password);
var msg = new System.Net.Mail.MailMessage(fromAddress, toAddress, "Subject", "Body");
System.Net.Mail.Attachment attachment =
AttachmentHelper.CreateAttachment(attachmentFilePath, displayName, TransferEncoding.Base64);
msg.Attachments.Add(attachment);
client.Send(msg);
}

You need to use the Quoted-Printable format for the attachment name:
C#: Class for decoding Quoted-Printable encoding?
http://www.codeproject.com/KB/security/TextCoDec.aspx

Related

Sendgrid dynamic data not being sent to Template

This is how I send my email:
public async System.Threading.Tasks.Task<string> SendInternalEmail(BasicEmailStructureViewModel Structure, List<string> AdditionalVariables, TenantEmailTemplate tenantEmailTemplate, TenantCommunication tenantCommunication, string ReceiverId, string ReceiverEmail, string ReceiverName, string CampaignName)
{
try
{
var client = new SendGridClient(tenantCommunication.SendgridApiKey);
var message = new SendGridMessage();
message.SetFrom(new EmailAddress(tenantCommunication.SendgridPrimarySender, tenantCommunication.SendgridPrimarySenderTag));
message.AddTo(new EmailAddress(ReceiverEmail, $"{ReceiverName}"));
message.Subject = tenantEmailTemplate.Subject;
message.SetTemplateId(tenantEmailTemplate.TemplateId);
List<string> jsonVars = new List<string>();
var subjectString = #$"""subject"":""{tenantEmailTemplate.Subject}""";
jsonVars.Add(subjectString);
foreach (PropertyInfo prop in Structure.GetType().GetProperties())
{
var variableString = #$"""{prop.Name}"":""{prop.GetValue(Structure, null)}""";
}
for (var i = 0; i < AdditionalVariables.Count; i++)
{
jsonVars.Add(AdditionalVariables[i]);
}
var flattenList = "{" + string.Join(",", jsonVars) + "}";
var emailData = Newtonsoft.Json.JsonConvert.DeserializeObject<object>(flattenList);
message.SetTemplateData(emailData);
if (CampaignName != null && CampaignName != "")
{
message.AddCustomArg("CampaignName", CampaignName);
}
var response = await client.SendEmailAsync(message);
if (response.IsSuccessStatusCode == true)
{
return Guid.NewGuid().ToString();
}
else
{
var errorMessage = response.Body.ReadAsStringAsync().Result;
return errorMessage;
}
}
catch(Exception e)
{
if (e != null)
{
return e.Message;
}
}
return "Invalid Email";
}
A typical input to this function will be like this:
var variableString =
#$"""verification_link"":""www.website.com?Key={Input.Key}""";
My email sends normally, however, none of the variables that I have set have been sent through. This is based roughly off the template sample on github: https://github.com/sendgrid/sendgrid-csharp/blob/main/examples/templates/templates.cs
Is there another sample I can use or what is the correct way to send variables dynamically?
I don't think that is the best way to construct the JSON for your dynamic template variables.
Would it be possible for you to build the variables as an object and then serialize them. Like:
var templateData = new {
subject = tenantEmailTemplate.Subjectm
otherVariableName = otherVariable
};
string emailData = JsonConvert.SerializeObject(templateData);
message.SetTemplateData(emailData);

SEndGrid not replacing Dynamics Data

My one template is not replacing dynamics data in the email
My Code is given below
public static System.Net.HttpStatusCode SendEmailV2(DynamicsModel dynamicsmodel, string templateId, TraceWriter log)
{
log.Info("Executing SendEmailV2");
var SendGridApiCode = System.Environment.GetEnvironmentVariable("SendGridApiCode", EnvironmentVariableTarget.Process);
var fromEmail = System.Environment.GetEnvironmentVariable("FromEmail", EnvironmentVariableTarget.Process);
var fromName = System.Environment.GetEnvironmentVariable("FromName", EnvironmentVariableTarget.Process);
var dynamicTemplateData = new DynamicData
{
Name = dynamicsmodel.FullName
};
string output = JsonConvert.SerializeObject(dynamicTemplateData);
log.Info("json:" + output);
EmailAddress from = new EmailAddress(fromEmail, "test name");
EmailAddress to = new EmailAddress(dynamicsmodel.Email, dynamicsmodel.FullName);
var sendGridClient = new SendGridClient(SendGridApiCode);
var sendGridMessage = CreateSingleTemplateEmail(from, to, templateId, dynamicTemplateData);
var response = sendGridClient.SendEmailAsync(sendGridMessage).Result;
if (response.StatusCode == System.Net.HttpStatusCode.Accepted)
{
log.Info("Emails Sent from SendGrid");
}
else
{
log.Info("response.StatusCode:" + response.StatusCode.ToString());
}
return response.StatusCode;
}
My JSON which is being passed is given below
{"name":"Test User"}
This happens to one template only. Any help will be much appreciated

Umbraco Decrypting using MachineKey Encoding in C#

I want to first encrypt some nodes in Umbraco's content editor. The code below is the one I use for encryption. I use MachineKey.Protect for this.
try
{
MailMessage message1 = new MailMessage();
MailMessage message2 = new MailMessage();
SmtpClient client = new SmtpClient();
string AfsenderEmail = model.Email;
string AfsenderNavn = model.Name;
string toAddress = Umbraco.Content(rootNode.Id).mailDerSendesTil;
message1.From = new MailAddress(toAddress);
message2.From = new MailAddress(toAddress);
message1.Subject = $"{Umbraco.Content(rootNode.Id).overskriftPaaDenMailViFaar}";
message1.Subject = message1.Subject.Replace("AfsenderEmail", AfsenderEmail);
message1.Subject = message1.Subject.Replace("AfsenderNavn", AfsenderNavn);
message1.Body = $"{Umbraco.Content(rootNode.Id).beskedViFaarNaarBeskedenSendes}";
message1.Body = message1.Body.Replace("AfsenderEmail", AfsenderEmail);
message1.Body = message1.Body.Replace("AfsenderNavn", AfsenderNavn);
message1.To.Add(new MailAddress(toAddress));
client.Send(message1);
message2.Subject = $"{Umbraco.Content(rootNode.Id).overskriftPaaMeddelelsenAfsenderenFaar}";
message2.Subject = message2.Subject.Replace("AfsenderEmail", AfsenderEmail);
message2.Subject = message2.Subject.Replace("AfsenderNavn", AfsenderNavn);
message2.Body = $"{Umbraco.Content(rootNode.Id).beskedAfsenderenFaarNaarBeskedenSendes}";
message2.Body = message2.Body.Replace("AfsenderEmail", AfsenderEmail);
message2.Body = message2.Body.Replace("AfsenderNavn", AfsenderNavn);
message2.To.Add(new MailAddress(AfsenderEmail));
client.Send(message2);
var beskederNode = Umbraco.TypedContentAtRoot().FirstOrDefault(x => x.ContentType.Alias.Equals("Besked"));
var encryptName = MachineKey.Protect(Encoding.ASCII.GetBytes(model.Name));
var encryptEmail = MachineKey.Protect(Encoding.ASCII.GetBytes(model.Email));
var encryptMessage = MachineKey.Protect(Encoding.ASCII.GetBytes(model.Message));
string nameEncrypted = Encoding.ASCII.GetString(encryptName);
string emailEncrypted = Encoding.ASCII.GetString(encryptEmail);
string messageEncrypted = Encoding.ASCII.GetString(encryptMessage);
var newContent = contentService.CreateContent(nameEncrypted, beskederNode.Id, "mails");
newContent.SetValue("fra", nameEncrypted);
newContent.SetValue("eMail", emailEncrypted);
newContent.SetValue("besked", messageEncrypted);
var result = contentService.SaveAndPublishWithStatus(newContent);
return new HttpStatusCodeResult(HttpStatusCode.OK);
}
catch (System.Exception ex)
{
Log.Error("Contact Form Error", ex);
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
}
This is where I Try to decrypt my code again. It throws an exception (System.Security.Cryptography.CryptographicException: 'Error occurred during a cryptographic operation.') when I call MachineKey.Unprotect(nameDecrypted) and I cannnot find my mistake. I think it maybe has somethimg to do with my Encoding and Decoding?
private void EditorModelEventManager_SendingContentModel(System.Web.Http.Filters.HttpActionExecutedContext sender, EditorModelEventArgs<Umbraco.Web.Models.ContentEditing.ContentItemDisplay> e)
{
var node = e.Model.Properties.ToList();
if (e.Model.IsChildOfListView && e.Model.ContentTypeAlias == "mails")
{
string nameDecrypt = node.Where(x => x.Alias.ToLower() == "fra").Select(x => x.Value).First().ToString();
Byte[] nameDecrypted = Encoding.ASCII.GetBytes(nameDecrypt);
var name = e.Model.Properties.FirstOrDefault(x => x.Alias.ToLower() == "fra");
Byte[] decryptName = MachineKey.Unprotect(nameDecrypted);
string nameReady = Encoding.ASCII.GetString(decryptName);
name.Value = $"{nameReady}";
}
}
}
}
I found a solution. Instead of using Encoding.ASCII.GetString(), I used Convert.FromBase64String().

newline not coming in mail body in Notes

I am new to Notes.
I am trying to send a mail from my application using Lotus Notes with an attachment,mail goes properly and attachment also goes but the problem is with the body content,the body looses its format and comes in a straight line
i expect as follows
Dear Sir,
please check the attachment.
Regards,
NewConcept Infotech Pvt.Ltd.,
but it comes like this
Dear Sir,please check the attachment.Regards,NewConcept Infotech Pvt.Ltd.,
i tried everything googled a lot but no use.
this is my code
public bool Email(string dbDirectory, string DataBase_Name, string Initialize_Pwd, string From, string To, string CC, string Bcc, string Subject, string body, string FileName, string LogFilePath)
{
bool msg = false;
dynamic EMailReplyTo = ConfigurationSettings.AppSettings["EMailReplyTo"];
NotesSession objNotesSession = new NotesSession();
NotesDatabase ndb = null;
NotesDocument ndoc = null;
NotesDbDirectory ndbD = null;
NotesStream LNStream;
NotesMIMEEntity LNBody;
object objAttach;
try
{
////--------------------Lotus Notes Connectivity-------------------------///
List<string> lstOutPutEmail = new List<string>();
lstOutPutEmail.Add(DataBase_Name);
lstOutPutEmail.Add(Initialize_Pwd);
objNotesSession.Initialize(lstOutPutEmail[1].ToString());
//// objNotesSession object Initialized
ndbD = objNotesSession.GetDbDirectory(dbDirectory);
ndb = objNotesSession.GetDatabase(dbDirectory, DataBase_Name, false);
//If the database is not already open then open it.
if (!ndb.IsOpen)
{
ndb.Open();
}
if (ndb != null)
{
ndoc = ndb.CreateDocument();
LNStream = objNotesSession.CreateStream();
LNBody = ndoc.CreateMIMEEntity();
// ndoc.ReplaceItemValue("SendBy", From);
ndoc.ReplaceItemValue("Form", "Memo");
ndoc.ReplaceItemValue("From", From);
ndoc.ReplaceItemValue("Principal", From);
ndoc.ReplaceItemValue("SendTo", To.Split(','));
if (CC != null)
{
if (CC != "")
{
ndoc.ReplaceItemValue("CopyTo", CC.Split(','));
}
}
if (Bcc != null)
{
if (Bcc != "")
{
ndoc.ReplaceItemValue("BlindCopyTo", Bcc.Split(','));
}
}
ndoc.ReplaceItemValue("Subject", Subject);
//
NotesRichTextItem objMailRTF = ndoc.CreateRichTextItem("Body");
ndoc.ReplaceItemValue("Body", body);
ndoc.SaveMessageOnSend = true;
if (FileName != "")
{
objAttach = objMailRTF.EmbedObject(Domino.EMBED_TYPE.EMBED_ATTACHMENT, "", FileName, "Attachment");
}
ndoc.Send(false);
ndbD = null;
objNotesSession = null;
ndb = null;
ndoc = null;
gl.runLogfile("Mail Send Successfuly To : " + To, LogFilePath);
}
msg = true;
}
catch (Exception ex)
{
Console.WriteLine("Error On sending Mail To : " + To);
gl.runLogfile("Error On sending Mail To : " + To, LogFilePath);
gl.runLogfile(ex.Message, LogFilePath);
msg = false;
}
finally
{
}
return msg;
}
Use RichTextItem's method AddNewLine() do add new lines to your Body field
NotesRichTextItem objMailRTF = ndoc.CreateRichTextItem("Body");
objMailRTF.AppendText("Dear Sir,");
objMailRTF.AddNewLine(1);
objMailRTF.AppendText("please check the attachment.");
objMailRTF.AddNewLine(2);
...
Delete the code line ndoc.ReplaceItemValue("Body", body); as it won't work otherwise.
0. MIME
If you are using MIME then you don't need to create the Body field. But you need to use <br> instead of newline characters.
//Remove this from your code:
//NotesRichTextItem objMailRTF = ndoc.CreateRichTextItem("Body");
//ndoc.ReplaceItemValue("Body", body);
objNotesSession.ConvertMIME = false;
LNStream.WriteText(body.Replace(Environment.NewLine, "<br>"));
LNBody.SetContentFromText(stream, "text/plain;charset=UTF-8", 1728);
ndoc.SaveMessageOnSend = true;
if (FileName != "")
{
//objAttach = objMailRTF.EmbedObject(Domino.EMBED_TYPE.EMBED_ATTACHMENT, "", FileName, "Attachment");
var child = LNBody.CreateChildEntity();
var header = child.CreateHeader("Content-Disposition");
header.SetHeaderValAndParams(string.Format("attachment; filename=\"{0}\""), Path.GetFileName(FileName));
LNStream = objNotesSession.CreateStream();
LNStream.Open(FileName, "binary");
child.SetContentFromBytes(LNStream, "application/octet-stream", 1730);
child.EncodeContent(1727);
ndoc.CloseMIMEEntities(True, "Body");
}
1. No MIME
If you don't want to use MIME then you must use AppendText method instead of ReplaceItemValue method:
NotesRichTextItem objMailRTF = ndoc.CreateRichTextItem("Body");
//ndoc.ReplaceItemValue("Body", body);
objMailRTF.AppendText(body);
ndoc.SaveMessageOnSend = true;
if (FileName != "")
{
objMailRTF = ndoc.CreateRichTextItem("Attachment");
objAttach = objMailRTF.EmbedObject(Domino.EMBED_TYPE.EMBED_ATTACHMENT, "", FileName, "Attachment");
}
In your ndoc.body, you have to code the newlines as "chr$(13)chr$(10)". In java (char)13(char)10 or \r\n.
You need to replace in the String body, you get in parameter, all occurence of newline (probably '\n' but look how it is coded in the String) to (char)13(char)10.
try:
body = body.replaceAll("\n", "\r\n");
ndoc.ReplaceItemValue("Body", body);
try will \\ if it doesn't work with 1 \ only.

Parse variable URI (RegEx, Uri, String-Functions?) c#

I'm writing a RTSP client module and for this, I need to parse an very variable URI. But I'm completely stuck about which method I should use (most-failsafe) and how to accomplish this.
An example URI could look like this:
rtsp://192.168.1.100:554/videocam/media/1/video/1
\_/ \_______________/\_______/\______________/
| | | |
scheme authority [sub] [mediacontrol]
But also other possibilities:
192.168.1.100/videocam/media/1/video/1
192.168.1.100:6000/media/1/video/1
192.168.1.100:6000/videocam
I need the following information:
IP | how can I recognise this pattern [num].[num].[num].[num]?
Port | easy if the string contains rtsp://, but what about just a number? 1-65555
Sub | Optional subpath, can completely vary!
MediaLevel | Optional MediaLevel (indicator for stream/track),
not to be confused with the path. MediaLevel can be also just like this: track1 or m1s3 or media1/video1.. see?
I can't go for the slash, also the path also can contain multiple slashes
Maybe there's a library for such tasks?
Thank you.
var uri = new Uri("rtsp://192.168.1.100:554/videocam/media/1/video/1");
var host = uri.Host;
var port = uri.Port;
var sub = uri.Segments[1];
var mlevel = uri.Segments.Skip(2).ToArray();
Here is a quick example of how to use the UriBuilder class. It is a bit verbose because it is an example and is not ready for production. If more subs are to be identified then they can be added to the Sub List as shown in the example.
class Program
{
private static string _scheme = string.Empty;
private static string _host = string.Empty;
private static string _sub = string.Empty;
static void Main(string[] args)
{
ParseUri("rtsp://192.168.1.100:554/videocam/media/1/video/1");
ParseUri("192.168.1.100/videocam/media/1/video/1");
ParseUri("192.168.1.100:6000/media/1/video/1");
ParseUri("192.168.1.100:6000/videocam");
// example of adding a new sub
Sub.Add("sample");
ParseUri("192.168.1.100:6000/sample/");
Console.ReadLine();
}
private static void ParseUri(string URI)
{
UriBuilder uri = new UriBuilder(URI);
_scheme = uri.Scheme;
_host = uri.Host;
_sub = string.Empty;
StringBuilder sb = new StringBuilder();
foreach (string s in uri.Uri.Segments)
{
if (Sub.Contains(s.Replace("/","")))
{_sub = s;}
else
{ sb.Append(s); }
}
Console.Out.WriteLine("+++++++++++++");
Console.Out.WriteLine("URI: {0}",URI);
Console.Out.WriteLine("Scheme: {0}", _scheme);
Console.Out.WriteLine("sub: {0}",_sub);
Console.Out.WriteLine("mediaControl: {0}", sb.ToString());
}
private static List<string> Sub
{
get
{
List<string> sub = new List<string>();
sub.Add("videocam");
return sub;
}
}
}
trace("Url : {0}", turl.Text);
var uri = new Uri(turl.Text);
string host = uri.Host;
int port = uri.Port;
string userInfo = uri.UserInfo;
string subStream = "";
string userName = "";
string password = "";
if (uri.Segments?.Any() == true)
{
subStream = string.Join("", uri.Segments);
}
if (!string.IsNullOrEmpty(userInfo))
{
if (userInfo.Contains(":"))
{
string[] data = userInfo.Split(':');
userName = data[0];
password = data[1];
}
else
{
userName = userInfo;
}
}
trace("host : {0}", host);
trace("port : {0}", port);
trace("userName : {0}", userName);
trace("password : {0}", password);
trace("subStream: {0}", subStream);

Categories