How to avoid embedded (inline) images when saving outlook email attachments - c#

I have written a method to save outlook email attachments into hard disk and convert the file into Base64String. When I was saving the attachments, the embedded (inline) images also getting saved, even though they are not REAL attachments. I wanted to save only the real attachments. Then I modified the method as follows. But now I'm getting an error from the line of ".OfType()".
Here is my code:
private string GetBase64StringForAttachments(Outlook.MailItem mailItem)
{
StringBuilder builder = new StringBuilder();
Outlook.Attachments mailAttachments = mailItem.Attachments;
try
{
if (mailAttachments != null)
{
Regex reg = new Regex(#"<img .+?>", RegexOptions.Singleline | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
MatchCollection matches = reg.Matches(mailItem.HTMLBody);
for (int i = 1; i <= mailAttachments.Count; i++)
{
Outlook.Attachment currentAttachment = mailAttachments[i];
bool isMatch = matches
.OfType<Match>()
.Select(m => m.Value)
.Where(s => s.IndexOf("cid:" + currentAttachment.FileName, StringComparison.InvariantCultureIgnoreCase) >= 0)
.Any();
MessageBox.Show(currentAttachment.FileName + ": " + (isMatch ? "Inline Image" : "Attached Image"));
if (currentAttachment != null)
{
string date = DateTime.Now.ToString("yyyymmddhhmmss");
string path = "C:\\test\\" + date + currentAttachment.FileName; //ToDo: Create Folder
currentAttachment.SaveAsFile(path);
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
byte[] filebytes = new byte[fs.Length];
fs.Read(filebytes, 0, Convert.ToInt32(fs.Length));
string encodedData = Convert.ToBase64String(filebytes, Base64FormattingOptions.InsertLineBreaks);
builder.Append(encodedData).Append(",");
Marshal.ReleaseComObject(currentAttachment);
}
}
if (builder.Length > 0)
{
string encodedAttachments = builder.ToString().Remove(builder.ToString().Length-1);
return builder.ToString();
}
else
return "";
}
else return "";
}
catch (Exception ex)
{
Debug.DebugMessage(2, "Error in GetBase64StringForAttachments : in AddinModule " + ex.Message);
return "";
}
finally
{
Marshal.ReleaseComObject(mailAttachments);
}
}
This is the Error Message:
'System.Text.RegularExpressions.MatchCollection' does not contain a definition for 'OfType' and no extension method 'OfType' accepting a first argument of type 'System.Text.RegularExpressions.MatchCollection' could be found (are you missing a using directive or an assembly reference?)
What I need:
I am not a big fan of LINQ. So, can you please advice me on this
Is there a better way of doing this?
I already tried followed the suggested answers for these questions and they did not work for me
Saving only the REAL attachments of an Outlook MailItem
Don't save embed image that contain into attachements (like signature image)
Is there a way to use Redemption for distinguishing the real attachments?

Yes, Redemption (I am its author) exposes the RDOAttachment.Hidden property - it checks the HTMLBody to make sure the attachment is not used as an inline image.
Also note that you can access the attachment data using RDOAtttachment.AsArray without saving the attachment as a file first.
Redemption.RDOSession rSession = new Redemption.RDOSession();
rSession.MAPIOBJECT = mailItem.Application.Session.MAPIOBJECT;
Redemption.RDOMail rMail= rSession.GetRDOFolderFromOutlookObject(mailItem)
foreach (Redemption.RDOAttachment attach in rMail.Attachments)
{
if ((attach.Type == Redemption.rdoAttachmentType.olByValue) && (!attach.Hidden))
{
attach.SaveAsFile(path);
}
}
next

using Attachment = MsgReader.Outlook.Storage.Attachment;
foreach (Attachment attachment in mailItem.Attachments.Where(a => ((Attachment)a).Hidden == false)) {
// do whatever you want with the 'real' attachments.
}

Related

Having issues saving email attachment to directory

I am working on an application that crawls my email and sniffs out any emails with attachments. All attachment are being returned in the order they were received. Now I want to go a step further and would like to save any attachments in a local directory. I have been looking for documentation or examples but I have come up empty. I will show you a snippet of my code
This Function will get Email Attachments
public static List<IMessage> GetEmailAttachments()
{
OutlookServicesClient star_Mail_Box = Start_OutLook_Services();
try
{
var Email_Box = star_Mail_Box.Users["*****#dell.com"].Folders["Inbox"].Messages.Where(m => m.HasAttachments == true).Expand(m => m.Attachments).ExecuteAsync();
var messages = Email_Box.Result.CurrentPage;
foreach (var message in messages.OrderByDescending(m=> m.DateTimeReceived))
{
var attachments = message.Attachments.CurrentPage;
foreach (var attachment in attachments)
{
///This is where I will need to put my Logic.
}
}
}
catch (Exception ex)
{
Console.WriteLine("Not Able To Get This Mail Box" + ex.Message + "\n\nDetails : \n\n " + ex.InnerException);
Console.ReadLine();
}
return null; // returning null right now for testing
}
Ok so after looking at the attachment definition I figured I go through a byte array to achieve what I want. Here goes the Some code for my attachment loop.
foreach (FileAttachment attachment in attachments)
{
byte[] bytefiles = attachment.ContentBytes;
string path = #"C:\Top-Level\" + attachment.Name;
if (!string.IsNullOrEmpty(message.Subject))
{
path = #"C:\Top-Level\" + message.Subject + "." + attachment.ContentType;
}
File.WriteAllBytes(path, bytefiles);
}

How to get multiple attachments from gmail and save them to the folder?

I am trying to get attachments from a mail and saving them to the folder of my choice. When a mail contains a single attachment, this saves to my folder perfectly regardless of the file type but if a mail contains multiple attachments, only the first attachment is saved to the folder. Please check my code to understand what the heck I am talking about.
#region Get External Attachments
requestMessage = service.Users.Messages.Get("me", messageId);
Message attMessage = requestMessage.Execute();
IList<MessagePart> parts = attMessage.Payload.Parts;
foreach (MessagePart p in parts)
{
if (p.Filename.Length > 0 && p.Filename != null)
{
string attid = p.Body.AttachmentId;
if (attid != null)
{
MessagePartBody attachPart = service.Users.Messages.Attachments.Get("me", messageId, attid).Execute();
byte[] data = FromBase64ForUrlString(attachPart.Data);
string decodedString = Convert.ToBase64String(data);
var dir = #"G:\" + messageId;
Console.WriteLine("FileName:"+" "+p.Filename);
// Console.WriteLine("MimeType:"+p.MimeType+" "+p.Filename);
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
File.WriteAllBytes(Path.Combine(dir, p.Filename), data);
}
}
}
}
#endregion
I have tested the above code with a mail with three attachments (jpg, excel file, and pdf), here jpg is the first attachment, it's the only attachment, which is saved in the folder. I want to save all attachment files to the folder.
Console.WriteLine("FileName:"+" "+p.Filename);
the above line returns the names of all attachments but my code is not saving them. Where did I go wrong?
Updated:
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
// File.WriteAllBytes(Path.Combine(dir, p.Filename), data);
}
File.WriteAllBytes(Path.Combine(dir, p.Filename), data);
after changing my code with above lines , everything works like a charm.
// The missing function from the solution above
public static byte[] FromBase64ForUrlString(string base64ForUrlInput)
{
int padChars = (base64ForUrlInput.Length % 4) == 0 ? 0 : (4 - (base64ForUrlInput.Length % 4));
StringBuilder result = new StringBuilder(base64ForUrlInput, base64ForUrlInput.Length + padChars);
result.Append(String.Empty.PadRight(padChars, '='));
result.Replace('-', '+');
result.Replace('_', '/');
return Convert.FromBase64String(result.ToString());
}

Acquire FLV URL from YouTube

There are a lot of questions of a very similar nature, but as of yet I'm unable to find anything which answers this question directly. A lot of responses include "look at the source code of this" and contain a link, unfortunately I'm struggling to analyse the source code in those links, so I'm hoping someone can give me something step by step.
Here is what I have so far:
Request link from end user (e.g www.youtube.com/watch?v=2FlgVN03fNM)
Send HTTP Request to URL, acquire source.
Regex the source to find some information (presumably a series of elements which can be concatenated to form a link to the FLV.
Download the FLV.
Convert the FLV into MP3 or whichever format you'd like.
I have already achieved steps 1, 2, 5, and they all do exactly what I want, but it seems there is a lack of clarity around how to do step 3. Could someone please break this down a little further? Example:
Regex for "XXXXXXXXX"
This line contains all information you need
Split the string by "YYY" to acquire a list of elements
Find all the elements which match "ZZZZZ"
Use those elements to create the FLV link
This would be very useful to me, and if possible I'd love steps or a very simple application in C# or PHP which extracts the link.
Here is a more concise implementation based on Pafy:
using System;
using System.Linq;
using HttpUtility = System.Web.HttpUtility;
using NameValueCollection = System.Collections.Specialized.NameValueCollection;
using WebClient = System.Net.WebClient;
class Program {
public static void Main(string[] args) {
string videoID = "2FlgVN03fNM";
string[] itagByPriority = {"5", "6", "34", "35"};
string videoUrl = "https://www.youtube.com/get_video_info?asv=3&el=detailpage&hl=en_US&video_id=" + videoID;
string encodedVideo = null;
using (var client = new WebClient()) {
encodedVideo = client.DownloadString(videoUrl);
}
NameValueCollection video = HttpUtility.ParseQueryString(encodedVideo);
string encodedStreamsCommaDelimited = video["url_encoded_fmt_stream_map"];
string[] encodedStreams = encodedStreamsCommaDelimited.Split(new char[]{','});
var streams = encodedStreams.Select(s => HttpUtility.ParseQueryString(s));
var streamsByPriority = streams.OrderBy(s => Array.IndexOf(itagByPriority, s["itag"]));
NameValueCollection preferredStream = streamsByPriority.LastOrDefault();
if (preferredStream != null) {
Console.WriteLine("{0}&signature={1}", preferredStream["url"], preferredStream["sig"]);
}
}
}
If you want to skip to a simple code sample for this, you can see the full source on GitHub: https://github.com/XtrmJosh/YouTubeDownloader
I was pretty much right with my assumptions, and although it's taken me 10 hours or so, I've finally achieved what I set out to do. Here is a rough breakdown:
Escape string to ensure we don't break anything
Run some fancy RegEx and what not to capture the exact areas of string we're looking for
Find signatures and what not in each of the URLs we find (we'll find a lot, and we need to narrow them down a fair bit before they can be used)
Add the signature we find for each URL to it, otherwise we get trash files
Scan for some itags, so we know what file type is associated with each link - I only want FLV files.
Append the video name to the URL, and download.
So here is the code I'm using to get all the video URLs in a HTML document (YouTube only - so far)
public static List<string> ExtractUrls(string html)
{
string title = GetTitle(html);
List<string> urls = new List<string>();
string DataBlockStart = "\"url_encoded_fmt_stream_map\":\\s+\"(.+?)&"; // Marks start of Javascript Data Block
html = Uri.UnescapeDataString(Regex.Match(html, DataBlockStart, RegexOptions.Singleline).Groups[1].ToString());
string firstPatren = html.Substring(0, html.IndexOf('=') + 1);
var matchs = Regex.Split(html, firstPatren);
for (int i = 0; i < matchs.Length; i++)
matchs[i] = firstPatren + matchs[i];
foreach (var match in matchs)
{
if (!match.Contains("url=")) continue;
string url = GetTxtBtwn(match, "url=", "\\u0026", 0, false);
if (url == "") url = GetTxtBtwn(match, "url=", ",url", 0, false);
if (url == "") url = GetTxtBtwn(match, "url=", "\",", 0, false);
string sig = GetTxtBtwn(match, "sig=", "\\u0026", 0, false);
if (sig == "") sig = GetTxtBtwn(match, "sig=", ",sig", 0, false);
if (sig == "") sig = GetTxtBtwn(match, "sig=", "\",", 0, false);
while ((url.EndsWith(",")) || (url.EndsWith(".")) || (url.EndsWith("\"")))
url = url.Remove(url.Length - 1, 1);
while ((sig.EndsWith(",")) || (sig.EndsWith(".")) || (sig.EndsWith("\"")))
sig = sig.Remove(sig.Length - 1, 1);
if (string.IsNullOrEmpty(url)) continue;
if (!string.IsNullOrEmpty(sig))
url += "&signature=" + sig;
urls.Add(url);
}
for (int i = 0; i < urls.Count; i++)
{
urls[i] += "&title=";
urls[i] += title;
}
return urls;
}
public static string GetTitle(string RssDoc)
{
string str14 = GetTxtBtwn(RssDoc, "'VIDEO_TITLE': '", "'", 0, false);
if (str14 == "") str14 = GetTxtBtwn(RssDoc, "\"title\" content=\"", "\"", 0, false);
if (str14 == "") str14 = GetTxtBtwn(RssDoc, "&title=", "&", 0, false);
str14 = str14.Replace(#"\", "").Replace("'", "'").Replace("\"", """).Replace("<", "<").Replace(">", ">").Replace("+", " ");
return str14;
}
public static string GetTxtBtwn(string input, string start, string end, int startIndex, bool UseLastIndexOf)
{
int index1 = UseLastIndexOf ? input.LastIndexOf(start, startIndex) :
input.IndexOf(start, startIndex);
if (index1 == -1) return "";
index1 += start.Length;
int index2 = input.IndexOf(end, index1);
if (index2 == -1) return input.Substring(index1);
return input.Substring(index1, index2 - index1);
}
This code will (with the current YouTube format) provide a link to an FLV file which you can download and do what you like with (within the TOS of YouTube). I then used this to find the highest quality link from those that this code provided:
public static string GetFLV(List<string> urls)
{
// Acquire a list of links which match the criteria for being FLV files
List<string> flvurls = new List<string>();
foreach (string url in urls)
{
string itag = Regex.Match(url, #"itag=([1-9]?[0-9]?[0-9])", RegexOptions.Singleline).Groups[1].ToString();
int itagint;
int.TryParse(itag, out itagint);
if (itagint == 5 || itagint == 6 || itagint == 34 || itagint == 35)
{
flvurls.Add(url);
}
}
// If we didn't find any FLVs, we return a fatal error and cause a bug later on
if (flvurls.Count == 0)
{
MessageBox.Show("Fatal error | iTag could not be found for FLV filetype. Please contact software vendor for assistance.");
return "";
}
// If we did find some FLVs, we need to find the highest quality FLV
else
{
#region findBestFLV
foreach (string url in flvurls)
{
string itag = Regex.Match(url, #"itag=([1-9]?[0-9]?[0-9])", RegexOptions.Singleline).Groups[1].ToString();
int itagint;
int.TryParse(itag, out itagint);
if (itagint == 35)
{
return url;
}
}
foreach (string url in flvurls)
{
string itag = Regex.Match(url, #"itag=([1-9]?[0-9]?[0-9])", RegexOptions.Singleline).Groups[1].ToString();
int itagint;
int.TryParse(itag, out itagint);
if (itagint == 34)
{
return url;
}
}
foreach (string url in flvurls)
{
string itag = Regex.Match(url, #"itag=([1-9]?[0-9]?[0-9])", RegexOptions.Singleline).Groups[1].ToString();
int itagint;
int.TryParse(itag, out itagint);
if (itagint == 6)
{
return url;
}
}
foreach (string url in flvurls)
{
string itag = Regex.Match(url, #"itag=([1-9]?[0-9]?[0-9])", RegexOptions.Singleline).Groups[1].ToString();
int itagint;
int.TryParse(itag, out itagint);
if (itagint == 5)
{
return url;
}
}
#endregion
}
MessageBox.Show("Fatal error | Something has gone horrible wrong whilst finding the best FLV to use. Run, brave warrior, for the end is near.");
return "";
}
Note it is very scrappy at the minute, the rest of the code is mostly snippets I've borrowed and edited slightly, but this bit I've written from my head in some rushed attempt to get something together for SOF.
Hope this helps someone else :)

System.IO exception coming while saving XML document in C#

After importing plenty of XML files into application i tried to do modifications on it by using XML document class, for this i created few methods to do modifications.
The thing is the starting method it's working fine and when comes to the second one it's displaying System.IO exception like "File is already using another process".
So any one help me out how can i solve this issue.
Sample code what i'm doing:
Method1(fileList);
Method2(fileList);
Method3(fileList);
private void Method1(IList<RenamedImportedFileInfo> fileList)
{
try
{
string isDefaultAttribute = Resource.Resources.ImportIsDefaultAttribute;
string editorsPath = editorsFolderName + Path.DirectorySeparatorChar + meterType;
string profilesPath = profileFolderName + Path.DirectorySeparatorChar + meterType;
string strUriAttribute = Resource.Resources.ImportUriAttribute;
foreach (RenamedImportedFileInfo renameInfo in fileList)
{
if (renameInfo.NewFilePath.ToString().Contains(editorsPath) && (renameInfo.IsProfileRenamed != true))
{
var xmldoc = new XmlDocument();
xmldoc.Load(renameInfo.NewFilePath);
if (xmldoc.DocumentElement.HasAttribute(isDefaultAttribute))
{
xmldoc.DocumentElement.Attributes[isDefaultAttribute].Value = Resource.Resources.ImportFalse;
}
XmlNodeList profileNodes = xmldoc.DocumentElement.GetElementsByTagName(Resource.Resources.ImportMeasurementProfileElement);
if (profileNodes.Count == 0)
{
profileNodes = xmldoc.DocumentElement.GetElementsByTagName(Resource.Resources.ImportBsMeasurementProfileElement);
}
if (profileNodes.Count > 0)
{
foreach (RenamedImportedFileInfo profileName in oRenamedImportedFileList)
{
if (profileName.NewFilePath.ToString().Contains(profilesPath))
{
if (string.Compare(Path.GetFileName(profileName.OldFilePath), Convert.ToString(profileNodes[0].Attributes[strUriAttribute].Value, CultureInfo.InvariantCulture), StringComparison.OrdinalIgnoreCase) == 0)
{
profileNodes[0].Attributes[strUriAttribute].Value = Path.GetFileName(profileName.NewFilePath);
renameInfo.IsProfileRenamed = true;
break;
}
}
}
}
xmldoc.Save(renameInfo.NewFilePath);
xmldoc = null;
profileNodes = null;
}
}
oRenamedImportedFileList = null;
}
catch (NullReferenceException nullException) { LastErrorMessage = nullException.Message; }
}
Thanks,
Raj
You are probably opening the same file twice in your application. Before you can open it again, you have to close it (or leave it open and work on the same document without opening it again).
For help on how to implement this, please show us more code so we can give you advice.

How do I append a User Mail signature in Outlook to an email created programmatically

I have an OL addin ( c# com using addin express) that is doing something like this
mailItem = (Outlook.MailItem)OutlookApp.CreateItem(Outlook.OlItemType.olMailItem);
mailItem.To = ReceipientEmailAddress;
mailItem.Subject = "SOME TEXT";
mailItem.Body = NewBody;
mailItem.Display(false);
This is however causing the default signature to disappear
i am assuming this is because a newBody is being set
I am not able to read the signature in any way or cause the mail creation to include the signature
oh this is OL 2007 .NET 2.0
I had same problem and found no answer, so I decided to solve this by myself getting the signature manually, this is what I did.
private string ReadSignature()
{
string appDataDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Microsoft\\Signatures";
string signature = string.Empty;
DirectoryInfo diInfo = new DirectoryInfo(appDataDir);
if (diInfo.Exists)
{
FileInfo[] fiSignature = diInfo.GetFiles("*.htm");
if (fiSignature.Length > 0)
{
StreamReader sr = new StreamReader(fiSignature[0].FullName, Encoding.Default);
signature = sr.ReadToEnd();
if (!string.IsNullOrEmpty(signature))
{
string fileName = fiSignature[0].Name.Replace(fiSignature[0].Extension, string.Empty);
signature = signature.Replace(fileName + "_files/", appDataDir + "/" + fileName + "_files/");
}
}
}
return signature;
}
Hope this helps.
This one work for me without any additional code.
olMail = outlook.CreateItem(0);
olMail.To = toEmailID;
olMail.Subject = "Subject";
if (attachments != null)
{
foreach (var path in attachments)
{
olMail.Attachments.Add(path);
}
}
olMail.Display();
//Display email first and then write body text to get original email template and signature text.
if (string.IsNullOrWhiteSpace(htmlBody))
{
if (!string.IsNullOrWhiteSpace(body))
{
olMail.Body = body + olMail.Body;
}
}
else
{
olMail.HTMLBody = htmlBody + olMail.HTMLBody;
}
Hope this helps.

Categories