MIME Attachment Names containing Extended Characters Fails - c#

While working on a project that emails files with international filenames, I've come across an unusual issue. if I attach with a US-ASCII filename only, I can get better than 200 characters long without errors.
If I include an extended character, it encodes in UTF-8 and the length before it gets funky is very small (< 40 characters). To define funky.. here's an example filename after it goes bad:
=utf-8BSU5GT1JNw4FUSUNBX0ltcGFjdF9Bc3Nl
it looks like UTF8 encoded string with a UTF-8 decoding instruction or a mime boundary... not sure which.
Has anyone seen this before -- and what are the rules / limitations of filenames -- I tried emailing the file by hand through outlook and it handles it, so I don't think it is a MIME specific limitation.
Sample code:
class Program
{
private const string DOMAIN = "foobar.com";
private const string SMTPHOST = "mail." + DOMAIN;
private const string FROM = "chadwick.posey#" + DOMAIN;
private const string TO = FROM;
static void Main(string[] args)
{
MailMessage msg = new MailMessage(FROM, TO, "Subject", "Body");
string path = Path.GetTempPath();
string name = "AAAAAA_AAAAAAAAAAAA_AAAAAAA_AAAA - IIIIIII CCCCCCCCCC DD IIIIIIÁIIII_20111018_091327.pptx";
File.WriteAllText(path + "\\" + name, "blah");
Attachment att = new Attachment(path + "\\" + name, new ContentType("application/vnd.openxmlformats-officedocument.presentationml.presentation"));
msg.Attachments.Add(att);
SmtpClient client = new SmtpClient(SMTPHOST, 25);
client.Send(msg);
}
}
I've tried (so far) -- setting the encoding for the attachment.NameEncoding to UTF8 and UTF32, neither worked. Setting the ContentDisposition.FileName on the attachment fails because it is not using US-ASCII characters only.
Any suggestions on how to get it to include the full filename with the accent / extended characters in tact?
Thanks
Chadwick

There is a hotfix from microsoft that seems to have done the trick for me.
Check http://support.microsoft.com/kb/2402064
On that page, there is a download link that will get you what you need.
Just install the correct version, depending on your system.

this should work:
Attachment att = new Attachment(#"c:\path to file\somename.txt",
System.Net.Mime.MediaTypeNames.Application.Octet);
//this itself should work.
att.Name = "история-болезни.doc"; // non-english filename
//if the above line doesn't make it work, try this.
att.Name = System.Web.HttpUtility.UrlEncode(att.Name, System.Text.Encoding.UTF8);
How to set the attatchment file name with chinese characters in C# SmtpClient programming?

Related

How to fix "Illegal characters in path." in file path?

I ran into an issue with writing a file path correctly to text file. I get an error "Illegal characters in path.".
My incoming file path from a function is
imagePath = "c:\temp\temp\file_name.jpg".(from a function)
Whenever I use the following
imagePath = Path.GetFullPath(imagePath);
I get the error "Illegal characters in path.".
The issue here is that "\t" is considered as an illegal character although its part of path. So how could I write this to a text file? I do not have control over such characters in the name.
How to write the full path ?
Here I am adding more details about the function.
ServerResponse jsonResult = new ServerResponse();
try
{
jsonResult = JsonConvert.DeserializeObject<ServerResponse>(strResponse);
string imagePath = jsonResult.image;
// string imagePath = "\"M\"\\a/ry/ h**ad:>> a\\/:*?\"| li*tt|le|| la\"mb.?";
// string invalid = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
imagePath = Path.GetFullPath(imagePath);
File.AppendAllText(PredictFileName, (string)imagePath);
txtJSONresult.AppendText((String)imagePath.ToString());
txtJSONresult.AppendText(Environment.NewLine);
}
catch (Exception ex)
{
txtJSONresult.AppendText(strResponse);
txtJSONresult.AppendText(Environment.NewLine);
txtJSONresult.AppendText(ex.Message);
txtJSONresult.AppendText(Environment.NewLine);
}
Server send the image path as "c:\temp\temp\file_name.jpg" I want to save that path to txt file in local PC. No matter I do I always have "\t" or probably other special characters that considered to be illegal.
string imagePath = jsonResult.image
So whats the right way to write this path to text file regardless of what it has?
Here is the JSON string I get from the server :
{"image":"c:\testimage\test.jpg","predictions":[[1.03891e-05, 0.0128408, 0.914102, 9.68333e-05, 0.0729495]]}
Fixing from server side
Ensure proper string formats are sent:
"C:\\doubleSlash\\paths"
#"C:\singleWith\atSymbol"
Fixing from client side and no server control
Masage data by replacing invalid characters with correct counterparts if possible, otherwise contact service provider about server side solution ;]

Web beacon trafficking with MimeKit

Is there any possible way to create a web beacon/bug with mimekit? I have taken a look on how to embed images within the message body in MimeKit but I can't seem to figure out how to transform this into a web beacon/bug, does anybody know a way how to do this?
My code:
private string makeMailContents(Klant klant, Mail mail)
{
var builder = new BodyBuilder();
var path = Path.Combine("wwwroot/trackers/track_open.png");
var img = builder.LinkedResources.Add(path);
byte[] data = Convert.FromBase64String(mail.Content);
string decoded = Encoding.UTF8.GetString(data);
Type mt = mail.GetType();
PropertyInfo mp;
string code;
string final = "";
img.ContentId = MimeUtils.GenerateMessageId();
return string.Format(#"<img src=""cid:{0}"">", img.ContentId);// this does not load anything, I want this to be the url to the image
}
fixed it, I just had to return the following:
return string.Format(#"<img src=""{0}"">", "http://localhost:5000/api/mail/track/open");
for some reason this only works with string.Format. You don't need MimeKit related functions to do this.
NOTE
The request only works once, at least in chrome, I believe that this is because of the cache

ZIP file from files with Danish characters in the filenames

I am trying to create a ZIP file from files with Danish characters in the filenames in C#. I have tried with both ICSharpCode, System.IO.Compression, and Ionic.Zip, but regardless I cannot get the Danish characters into the ZIP file.
I need the filenames to be exactly the same as the originals, because I am uploading the ZIP files to a program I have no control over.
It looks as though I should choose that the filenames be saved as Unicode with newEntry.IsUnicodeText = true but this gives me something like +ª+++Ñ+å+ÿ+à instead of æøåÆØÅ when I open the zipfile in Windows Explorer. With IsUnicodeText = false I get æ¢åƥŠwhich is close - only the ø becomes a cent character.
I get the same result from System.IO.Compression, and Ionic.Zip if I choose UTF8 encoding.
( I have also tried ZipConstants.DefaultCodePage = 850 - this does not help )
I can see that people have been struggling with this for years, but I don't see any clear answer. I would be grateful for any tips.
// this is the ICSharpCode version
string fileToZip = #"Kontrolplan_15_Kørestrøm.docx"; // a file
string entryName = "Danish Letters (æøåÆØÅ).docx";
string zipPath = #"ZsharpZIP.ZIP";
using (ICSharpCode.SharpZipLib.Zip.ZipOutputStream s = new ICSharpCode.SharpZipLib.Zip.ZipOutputStream(File.Create(zipPath)))
{
byte[] buffer = new byte[4096];
// ---- store statusPath
FileInfo fi = new FileInfo(fileToZip);
ICSharpCode.SharpZipLib.Zip.ZipEntry newEntry = new ICSharpCode.SharpZipLib.Zip.ZipEntry(entryName);
newEntry.Size = fi.Length;
newEntry.IsUnicodeText = true;
s.PutNextEntry(newEntry);
// write file
using (FileStream streamReader = File.OpenRead(fileToZip))
{
StreamUtils.Copy(streamReader, s, buffer);
}
s.CloseEntry();
It seems that Windows Explorer's built-in zip utility cannot handle UTF-8 file names. When I used 7-Zip instead, the problem disappeared.

Sending Swedish and Chinese signs to Docx using OpenXML and RTF

Goal
Passing Swedish and Chinese signs to a DocX-file in a RTF format.[2]
Description
I need to dynamically generate a RTF-formatted string containing Swedish and Chinese signs and send it to an existing Docx-file. I have managed to handle the Swedish diaereses (åäö) but I can't manage to get the Chinese signs to be shown properly, instead they are shown as ????
private void buttonSendDiaeresesToDocx_Click(object sender, EventArgs e)
{
var desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
var filename = #"SpecialCharactersInDocx.docx";
var filepath = Path.Combine(desktop, filename);
//Dynamic content fetched from the database.
var content = "This should be Swedish and Chinese signs -> åäö - 部件名称";
var rtfEncodedString = new StringBuilder();
rtfEncodedString.Append(#"{\rtf1\ansi{\fonttbl\f0\fswiss Helvetica;}\f0\pard ");
rtfEncodedString.Append(content);
rtfEncodedString.Append(#"\par}");
removeExistingFile(filepath);
createEmptyDocx(filepath);
addRtfToWordDocument(filepath, rtfEncodedString.ToString());
openDocx(filepath);
}
private void addRtfToWordDocument(string filepath, string rtfEncodedString)
{
//Implemented as suggested at
//http://stackoverflow.com/a/14861397/1997617
using (WordprocessingDocument doc = WordprocessingDocument.Open(filepath, true))
{
string altChunkId = "AltChunkId1";
MainDocumentPart mainDocPart = doc.MainDocumentPart;
AlternativeFormatImportPart chunk = mainDocPart.AddAlternativeFormatImportPart(
AlternativeFormatImportPartType.Rtf, altChunkId);
using (MemoryStream ms = new MemoryStream(Encoding.Default.GetBytes(rtfEncodedString)))
{
chunk.FeedData(ms);
}
AltChunk altChunk = new AltChunk();
altChunk.Id = altChunkId;
mainDocPart.Document.Body.ReplaceChild(
altChunk, mainDocPart.Document.Body.Elements<Paragraph>().Last());
mainDocPart.Document.Save();
}
}
I have tried to use different encodings for the memory stream (Default, ASCII, UTF8, GB18030, ...) but none seams to work. I've also tried to convert the encoding of the rtfEncodedString variable before passing it to the addRtfToWordDocument method.
How do I make both the Swedish and the Chinese signs to show properly in the document?
Notes and references
The above code snippet is the part of my solution that I think is relevant for the question. The entire code sample can be downloaded at http://www.bjornlarsson.se/externals/SpecialCharactersInDocx02.zip
The RTF format is needed in the real world application since the content is to be shown as a table (with bold text) in the document.
You could use wordpad to create the rtf string for you. Open wordpad copy your content save to file. And then use a texteditor to read the rtf.
your rtf string then looks like this :
{\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1031{\fonttbl{\f0\fnil Consolas;}{\f1\fnil\fcharset0 Consolas;}{\f2\fnil\fcharset134 SimSun;}{\f3\fnil\fcharset0 Calibri;}}
{\*\generator Riched20 10.0.10586}\viewkind4\uc1
\pard\sa200\sl276\slmult1\f0\fs19\lang7 This should be Swedish and Chinese signs -> \f1\'e5\'e4\'f6 - \f2\'b2\'bf\'bc\'fe\'c3\'fb\'b3\'c6\f3\fs22\par
}
maybe it helps.I tested the rtf string with your code and it works!
Dynamic generate rtf string via richtextbox :
private void buttonSendDiaeresesToDocx_Click(object sender, EventArgs e)
{
var desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
var filename = #"SpecialCharactersInDocx.docx";
var filepath = Path.Combine(desktop, filename);
removeExistingFile(filepath);
createEmptyDocx(filepath);
rtfEncodedString = new StringBuilder();
string contentOriginal = "This should be Swedish and Chinese signs -> åäö - 部件名称";
string rtfStart =
"{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1031{\\fonttbl{\\f0\\fnil\\fcharset0 Microsoft Sans Serif;}{\\f1\\fmodern\\fprq6\\fcharset134 SimSun;}}\r\n\\viewkind4\\uc1\\pard\\f0\\fs17 ";
RichTextBox rtfBox = new RichTextBox {Text = contentOriginal};
string content = rtfBox.Rtf;
content = content.Replace(rtfStart, "");
rtfEncodedString.Append(rtfStart);
rtfEncodedString.Append(content);
rtfEncodedString.Append(#"\par}");
addRtfToWordDocument(filepath, rtfEncodedString.ToString());
openDocx(filepath);
}

How can I create a temp file with a specific extension with .NET?

I need to generate a unique temporary file with a .csv extension.
What I do right now is
string filepath = System.IO.Path.GetTempFileName().Replace(".tmp", ".csv");
However, this doesn't guarantee that my .csv file will be unique.
I know the chances I ever got a collision are very low (especially if you consider that I don't delete the .tmp files), but this code doesn't looks good to me.
Of course I could manually generate random file names until I eventually find a unique one (which shouldn't be a problem), but I'm curious to know if others have found a nice way to deal with this problem.
Guaranteed to be (statistically) unique:
string fileName = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".csv";
(To quote from the wiki article on the probabilty of a collision:
...one's annual risk of being hit by a
meteorite is estimated to be one
chance in 17 billion [19], that means
the probability is about 0.00000000006
(6 × 10−11), equivalent to the odds of
creating a few tens of trillions of
UUIDs in a year and having one
duplicate. In other words, only after
generating 1 billion UUIDs every
second for the next 100 years, the
probability of creating just one
duplicate would be about 50%. The
probability of one duplicate would be
about 50% if every person on earth
owns 600 million UUIDs
EDIT: Please also see JaredPar's comments.
Try this function ...
public static string GetTempFilePathWithExtension(string extension) {
var path = Path.GetTempPath();
var fileName = Path.ChangeExtension(Guid.NewGuid().ToString(), extension);
return Path.Combine(path, fileName);
}
It will return a full path with the extension of your choice.
Note, it's not guaranteed to produce a unique file name since someone else could have technically already created that file. However the chances of someone guessing the next guid produced by your app and creating it is very very low. It's pretty safe to assume this will be unique.
public static string GetTempFileName(string extension)
{
int attempt = 0;
while (true)
{
string fileName = Path.GetRandomFileName();
fileName = Path.ChangeExtension(fileName, extension);
fileName = Path.Combine(Path.GetTempPath(), fileName);
try
{
using (new FileStream(fileName, FileMode.CreateNew)) { }
return fileName;
}
catch (IOException ex)
{
if (++attempt == 10)
throw new IOException("No unique temporary file name is available.", ex);
}
}
}
Note: this works like Path.GetTempFileName. An empty file is created to reserve the file name. It makes 10 attempts, in case of collisions generated by Path.GetRandomFileName();
You can also alternatively use System.CodeDom.Compiler.TempFileCollection.
string tempDirectory = #"c:\\temp";
TempFileCollection coll = new TempFileCollection(tempDirectory, true);
string filename = coll.AddExtension("txt", true);
File.WriteAllText(Path.Combine(tempDirectory,filename),"Hello World");
Here I used a txt extension but you can specify whatever you want. I also set the keep flag to true so that the temp file is kept around after use. Unfortunately, TempFileCollection creates one random file per extension. If you need more temp files, you can create multiple instances of TempFileCollection.
The MSDN documentation for C++'s GetTempFileName discusses your concern and answers it:
GetTempFileName is not able to guarantee that the file name is unique.
Only the lower 16 bits of the uUnique parameter are used. This limits GetTempFileName to a maximum of 65,535 unique file names if the lpPathName and lpPrefixString parameters remain the same.
Due to the algorithm used to generate file names, GetTempFileName can perform poorly when creating a large number of files with the same prefix. In such cases, it is recommended that you construct unique file names based on GUIDs.
Why not checking if the file exists?
string fileName;
do
{
fileName = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".csv";
} while (System.IO.File.Exists(fileName));
You can also do the following
string filepath = Path.ChangeExtension(Path.GetTempFileName(), ".csv");
and this also works as expected
string filepath = Path.ChangeExtension(Path.GetTempPath() + Guid.NewGuid().ToString(), ".csv");
How about:
Path.Combine(Path.GetTempPath(), DateTime.Now.Ticks.ToString() + "_" + Guid.NewGuid().ToString() + ".csv")
It is highly improbable that the computer will generate the same Guid at the same instant of time. The only weakness i see here is the performance impact DateTime.Now.Ticks will add.
In my opinion, most answers proposed here as sub-optimal. The one coming closest is the original one proposed initially by Brann.
A Temp Filename must be
Unique
Conflict-free (not already exist)
Atomic (Creation of Name & File in the same operation)
Hard to guess
Because of these requirements, it is not a godd idea to program such a beast on your own. Smart People writing IO Libraries worry about things like locking (if needed) etc.
Therefore, I see no need to rewrite System.IO.Path.GetTempFileName().
This, even if it looks clumsy, should do the job:
//Note that this already *creates* the file
string filename1 = System.IO.Path.GetTempFileName()
// Rename and move
filename = filename.Replace(".tmp", ".csv");
File.Move(filename1 , filename);
I mixed #Maxence and #Mitch Wheat answers keeping in mind I want the semantic of GetTempFileName method (the fileName is the name of a new file created) adding the extension preferred.
string GetNewTempFile(string extension)
{
if (!extension.StartWith(".")) extension="." + extension;
string fileName;
bool bCollisions = false;
do {
fileName = Path.Combine(System.IO.Path.GetTempPath(), Guid.NewGuid().ToString() + extension);
try
{
using (new FileStream(fileName, FileMode.CreateNew)) { }
bCollisions = false;
}
catch (IOException)
{
bCollisions = true;
}
}
while (bCollisions);
return fileName;
}
This could be handy for you... It's to create a temp. folder and return it as a string in VB.NET.
Easily convertible to C#:
Public Function GetTempDirectory() As String
Dim mpath As String
Do
mpath = System.IO.Path.Combine(System.IO.Path.GetTempPath, System.IO.Path.GetRandomFileName)
Loop While System.IO.Directory.Exists(mpath) Or System.IO.File.Exists(mpath)
System.IO.Directory.CreateDirectory(mpath)
Return mpath
End Function
This seems to work fine for me: it checks for file existance and creates the file to be sure it's a writable location.
Should work fine, you can change it to return directly the FileStream (which is normally what you need for a temp file):
private string GetTempFile(string fileExtension)
{
string temp = System.IO.Path.GetTempPath();
string res = string.Empty;
while (true) {
res = string.Format("{0}.{1}", Guid.NewGuid().ToString(), fileExtension);
res = System.IO.Path.Combine(temp, res);
if (!System.IO.File.Exists(res)) {
try {
System.IO.FileStream s = System.IO.File.Create(res);
s.Close();
break;
}
catch (Exception) {
}
}
}
return res;
} // GetTempFile
Based on answers I found from the internet, I come to my code as following:
public static string GetTemporaryFileName()
{
string tempFilePath = Path.Combine(Path.GetTempPath(), "SnapshotTemp");
Directory.Delete(tempFilePath, true);
Directory.CreateDirectory(tempFilePath);
return Path.Combine(tempFilePath, DateTime.Now.ToString("MMddHHmm") + "-" + Guid.NewGuid().ToString() + ".png");
}
And as C# Cookbook by Jay Hilyard, Stephen Teilhet pointed in Using a Temporary File in Your Application:
you should use a temporary file whenever you need to store
information temporarily for later retrieval.
The one thing you must remember is to delete this temporary file
before the application that created it is terminated.
If it is not deleted, it will remain in the user’s temporary
directory until the user manually deletes it.
This is what I am doing:
string tStamp = String.Format("{0:yyyyMMdd.HHmmss}", DateTime.Now);
string ProcID = Process.GetCurrentProcess().Id.ToString();
string tmpFolder = System.IO.Path.GetTempPath();
string outFile = tmpFolder + ProcID + "_" + tStamp + ".txt";
This is a simple but effective way to generate incremental filenames. It will look in the current directly (you can easily point that somewhere else) and search for files with the base YourApplicationName*.txt (again you can easily change that). It will start at 0000 so that the first file name will be YourApplicationName0000.txt. if for some reason there are file names with junk between (meaning not numbers) the left and right parts, those files will be ignored by virtue of the tryparse call.
public static string CreateNewOutPutFile()
{
const string RemoveLeft = "YourApplicationName";
const string RemoveRight = ".txt";
const string searchString = RemoveLeft + "*" + RemoveRight;
const string numberSpecifier = "0000";
int maxTempNdx = -1;
string fileName;
string [] Files = Directory.GetFiles(Directory.GetCurrentDirectory(), searchString);
foreach( string file in Files)
{
fileName = Path.GetFileName(file);
string stripped = fileName.Remove(fileName.Length - RemoveRight.Length, RemoveRight.Length).Remove(0, RemoveLeft.Length);
if( int.TryParse(stripped,out int current) )
{
if (current > maxTempNdx)
maxTempNdx = current;
}
}
maxTempNdx++;
fileName = RemoveLeft + maxTempNdx.ToString(numberSpecifier) + RemoveRight;
File.CreateText(fileName); // optional
return fileName;
}
Easy Function in C#:
public static string GetTempFileName(string extension = "csv")
{
return Path.ChangeExtension(Path.GetTempFileName(), extension);
}
In this what we can do we can first find the extension of file
which is coming from file and after finding its extension.Then we
can create the temprary name of file and after that we can change
extension by the previous one it will works.
var name = Path.GetTempFileName();
var changename = Path.GetFileName(name);
var fileName = Path.ChangeExtension(changename, fileExtension);
I think you should try this:
string path = Path.GetRandomFileName();
path = Path.Combine(#"c:\temp", path);
path = Path.ChangeExtension(path, ".tmp");
File.Create(path);
It generates a unique filename and creates a file with that file name at a specified location.

Categories

Resources