I am trying to find a solution to distinguish between an embedded image and attachment in Outlook mail. After doing some research, i found the following code works for most of the case
foreach (Outlook.Attachment attachment in mailItem.Attachments)
{
try
{
var attachmentType = attachment.FileName.Substring(attachment.FileName.LastIndexOf('.'));
if (attachmentType!=null&&attachmentType.Trim().Length>1&&_fileTypeFilter.Contains(attachmentType.Substring(1).ToLower()))
{
prop=attachment.PropertyAccessor;
string conentId = (string)prop.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x3712001E");
if ((attachmentType.Substring(1).ToLower() == "pdf") ||(conentId==null||conentId.Trim().Length==0))
{
//Always allow PDF
// This is an attachement
}
}
}
catch (Exception ex)
{
}
}
The issue is when a mail is send from other mail systems (for eg: hotmail ) then the content id is not null for attachments. This cause the attachments to be ignored .
Another suggestion i tired is to check the property based on following StackFlow don't save embedded
foreach (Outlook.Attachment attachment in mailItem.Attachments)
{
try
{
// var tst = attachment.Type;
var attachmentType = attachment.FileName.Substring(attachment.FileName.LastIndexOf('.'));
if (attachmentType!=null&&attachmentType.Trim().Length>1&&_fileTypeFilter.Contains(attachmentType.Substring(1).ToLower()))
{
prop=attachment.PropertyAccessor;
string conentId = (string)prop.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x3712001E");
var flags = prop.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x37140003");
var asize = attachment.Size;
if ((attachmentType.Substring(1).ToLower() == "pdf") ||
(asize>0&&(flags!=4 &&(int)attachment.Type != 6))) // As per present understanding - If rtF mail attachment comes here - and the embeded image is treated as attachment then Type value is 6 and ignore it
// (conentId==null||conentId.Trim().Length==0))
{
//This is a valid attachment
}
}
}
catch (Exception ex)
{
}
}
But this sometimes includes the image in signature
the most reliable way is to parse the HTML body (MailItem.HTMLBody property) to extract all img tags and check their scr attributes. If its is of the form ""cid:xyz", then "xyz" will be the value of the PR_ATTACH_CONTENT_ID property on the attachment. It can also refer to the graphics file by its file name.
Here is the working solution
var selection = explorer.Selection;
if (selection.Count == 1)
{
object selectedItem = selection[1];
var mailItem = selectedItem as Outlook.MailItem;
if (mailItem == null) return;
foreach (Outlook.Attachment attachment in mailItem.Attachments)
{
bool validAttachment = isAnAttachment(attachment);
}
}
private bool isAnAttachment(Outlook.Attachment attachment)
{
bool isValid = false;
var attachmentType = attachment.FileName.Substring(attachment.FileName.LastIndexOf('.'));
if (attachmentType != null && attachmentType.Trim().Length > 1 && _fileTypeFilter.Contains(attachmentType.Substring(1).ToLower()))
{
Outlook.PropertyAccessor prop = attachment.PropertyAccessor;
var flags = prop.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x37140003");
var asize = attachment.Size;
// As per present understanding - If rtF mail attachment comes here - and the embeded image is treated as attachmet
if ((attachmentType.Substring(1).ToLower() == "pdf") || (asize > 0 && flags != 4 && (int)attachment.Type != 6))
{
isValid = true;
}
}
return isValid;
}
Related
I have this code that actually works in all cases except when the image is sent by an IOS device, in that case attachments appears in blank.
I connect trough IMAP and take UIDS, then I extract information about email, save attachments and save entire email.
Everything is fine except when the sender attach documents or images by IOS, in that cases the code cant find any documents.
What can I do ?
Thanks
try {
IMailFolder mailFolder = imapClient.GetFolder(Folder);
mailFolder.Open(FolderAccess.ReadOnly);
MimeMessage m = mailFolder.GetMessage(new UniqueId(Decimal.ToUInt32(UID)));
//MailMessage m = imapClient.GetMessage(UID, false, false);
Subject = m.Subject;
From_Name = (m.Sender != null) ? m.Sender.ToString() : "";//m.Sender.DisplayName;
From_Address = (m.From != null) ? m.From.ToString() : "";
Cc_Address = (m.Cc != null) ? m.Cc.ToString() : "";
Date_Sent = m.Date.DateTime;
MessageID = m.MessageId;
Body = m.TextBody;
m.WriteTo (EmailName);
if ( Save_Attachments && Attachments_Path != "") {
System.IO.Directory.CreateDirectory(Attachments_Path);
DataTable dt = new DataTable();
dt.Columns.Add("Path", typeof(String));
foreach (MimeEntity attachment in m.Attachments)
{
if (attachment is MimePart)
{
MimePart part = (MimePart)attachment;
string path = System.IO.Path.Combine(Attachments_Path, removeIllegal(part.FileName));
using (var stream = File.Create(path))
{
part.Content.DecodeTo(stream);
}
DataRow dr = dt.NewRow();
dr["Path"] = path;
dt.Rows.Add(dr);
}
}
Attachments = dt;
} else {
Attachments = null;
}
}
catch (Exception ex) {
Subject = "";
From_Name = "";
From_Address = "";
Cc_Address = "";
Date_Sent = new DateTime();
MessageID = "";
Body = "";
Attachments = null;
throw ex;
}
Instead of using MimeMessage.Attachments, you need to use MimeMessage.BodyParts - but be aware that BodyParts also contains the text body of the message.
I have more information here: http://www.mimekit.net/docs/html/Working-With-Messages.htm
A quick & dirty way of accomplishing what you want to do is probably something like this:
foreach (var attachment in m.BodyParts.Where (x => x.ContentDisposition?.FileName != null))
(Note: you'll need to make sure to add using System.Linq; at the top of your C# file)
I have in my Outlook 2010-Add-In (c#) many folders. They are in my private post box or in one of my shared post boxes.
Now I am looking for a solution to find out, how to get the right email address (sender / recipient) associated with a dedicated folder. It could be any folder from my private or anyone of my shared post boxes.
I think, maybe I could use the EntryId / StoreId from the folder item to identify the corresponding email address.
I know already, that I could get the email address from any mail item but I'm not looking for this solution.
I like to answer my own questions: I think that I've found a plausible solution.
I do not treat any exceptions inside the function, I do that from outside.
private string GetSMTPAddressByFolderItem(Outlook.MAPIFolder mapiFolder)
{
string PR_MAILBOX_OWNER_ENTRYID = #"http://schemas.microsoft.com/mapi/proptag/0x661B0102";
string PR_SMTP_ADDRESS = #"http://schemas.microsoft.com/mapi/proptag/0x39FE001E";
Outlook.Store store = null;
Outlook.NameSpace ns = null;
Outlook.AddressEntry sender = null;
Outlook._ExchangeUser exchUser = null;
try
{
if (mapiFolder == null)
{
return null;
}
// Get the parent store.
store = mapiFolder.Store;
string storeOwnerEntryId = store.PropertyAccessor.BinaryToString(store.PropertyAccessor.GetProperty(PR_MAILBOX_OWNER_ENTRYID)) as string;
ns = Application.GetNamespace(Constants.OL_NAMESPACE); // i.e. "MAPI"
sender = ns.GetAddressEntryFromID(storeOwnerEntryId);
if (sender != null)
{
if (sender.AddressEntryUserType == Outlook.OlAddressEntryUserType.olExchangeUserAddressEntry ||
sender.AddressEntryUserType == Outlook.OlAddressEntryUserType.olExchangeRemoteUserAddressEntry)
{
exchUser = sender.GetExchangeUser();
if (exchUser != null)
{
return exchUser.PrimarySmtpAddress;
}
else
{
return null;
}
}
else
{
return sender.PropertyAccessor.GetProperty(PR_SMTP_ADDRESS) as string;
}
}
return null;
}
finally
{
if (ns != null)
{
Marshal.ReleaseComObject(ns);
ns = null;
}
if (store != null)
{
Marshal.ReleaseComObject(store);
store = null;
}
if (sender != null)
{
Marshal.ReleaseComObject(sender);
sender = null;
}
if (exchUser != null)
{
Marshal.ReleaseComObject(exchUser);
exchUser = null;
}
}
}
I'm using MailKit library to handle emails, which has been working well. However, I'm trying to split emails into their constituent files a) Main email (no attachments) b) Individual attachment files, to store on the filesystem.
I can save the attachments individually, but can't seem to remove them from the email body code. I.e. they're getting saved along with the main email, so duplicating data. :/
I've tried:
foreach (MimePart part in inMessage.BodyParts)
{
if (part.IsAttachment)
{
// Remove MimePart < This function isn't available on the collection.
}
}
Have also tried:
var builder = new BodyBuilder();
foreach (MimePart part in inMessage.BodyParts)
{
if (!part.IsAttachment)
{
// Add MimeParts to collection < This function isn't available on the collection.
}
}
outMessage.Body = builder.ToMessageBody();
If anyone can help with this, I'd much appreciate it.
Solution implemented FYI:
private string GetMimeMessageOnly(string outDirPath)
{
MimeMessage message = (Master as fsEmail).GetMimeMessage();
if (message.Attachments.Any())
{
var multipart = message.Body as Multipart;
if (multipart != null)
{
while (message.Attachments.Count() > 0)
{
multipart.Remove(message.Attachments.ElementAt(0));
}
}
message.Body = multipart;
}
string filePath = outDirPath + Guid.NewGuid().ToString() + ".eml";
Directory.CreateDirectory(Path.GetDirectoryName(outDirPath));
using (var cancel = new System.Threading.CancellationTokenSource())
{
using (var stream = File.Create(filePath))
{
message.WriteTo(stream, cancel.Token);
}
}
return filePath;
}
And to get the attachments only:
private List<string> GetAttachments(string outDirPath)
{
MimeMessage message = (Master as fsEmail).GetMimeMessage();
List<string> list = new List<string>();
foreach (MimePart attachment in message.Attachments)
{
using (var cancel = new System.Threading.CancellationTokenSource())
{
string filePath = outDirPath + Guid.NewGuid().ToString() + Path.GetExtension(attachment.FileName);
using (var stream = File.Create(filePath))
{
attachment.ContentObject.DecodeTo(stream, cancel.Token);
list.Add(filePath);
}
}
}
return list;
}
You could retrieve all MimeParts that are attachments https://github.com/jstedfast/MimeKit/blob/master/MimeKit/MimeMessage.cs#L734 and then iterate over the all Multiparts and call https://github.com/jstedfast/MimeKit/blob/master/MimeKit/Multipart.cs#L468 for the attachments to remove.
The sample below makes a few assumptions about the mail e.g. there is only one Multipart some email client (Outlook) are very creative how mails are crafted.
static void Main(string[] args)
{
var mimeMessage = MimeMessage.Load(#"x:\sample.eml");
var attachments = mimeMessage.Attachments.ToList();
if (attachments.Any())
{
// Only multipart mails can have attachments
var multipart = mimeMessage.Body as Multipart;
if (multipart != null)
{
foreach(var attachment in attachments)
{
multipart.Remove(attachment);
}
}
mimeMessage.Body = multipart;
}
mimeMessage.WriteTo(new FileStream(#"x:\stripped.eml", FileMode.CreateNew));
}
Starting with MimeKit 0.38.0.0, you'll be able to use a MimeIterator to traverse the MIME tree structure to collect a list of attachments that you'd like to remove (and remove them). To do this, your code would look something like this:
var attachments = new List<MimePart> ();
var multiparts = new List<Multipart> ();
var iter = new MimeIterator (message);
// collect our list of attachments and their parent multiparts
while (iter.MoveNext ()) {
var multipart = iter.Parent as Multipart;
var part = iter.Current as MimePart;
if (multipart != null && part != null && part.IsAttachment) {
// keep track of each attachment's parent multipart
multiparts.Add (multipart);
attachments.Add (part);
}
}
// now remove each attachment from its parent multipart...
for (int i = 0; i < attachments.Count; i++)
multiparts[i].Remove (attachments[i]);
I created an application, that downloads emails and attachments as well using Mailkit.
I faced one problem: E-Mails sent from iOS with attached pictures were not processed correctly. MailKit did not add the images to the Attachments list.
I used this method to get only the text of the message:
private static string GetPlainTextFromMessageBody(MimeMessage message)
{
//content type needs to match text/plain otherwise i would store html into DB
var mimeParts = message.BodyParts.Where(bp => bp.IsAttachment == false && bp.ContentType.Matches("text", "plain"));
foreach (var mimePart in mimeParts)
{
if (mimePart.GetType() == typeof(TextPart))
{
var textPart = (TextPart)mimePart;
return textPart.Text;
}
}
return String.Empty;
}
This is the method I used to download only the .jpg files:
foreach (var attachment in message.BodyParts.Where(bp => !string.IsNullOrEmpty(bp.FileName)))
{
if (attachment.FileName.ToLowerInvariant().EndsWith(".jpg"))
{
//do something with the image here
}
}
I'm attempting to get the email address typed into the To field of a compose mail window.
I try to get the Address property of a Recipient, which according to VS, should give me the email.
I am instead receiving a string that looks like this:
"/c=US/a=att/p=Microsoft/o=Finance/ou=Purchasing/s=Furthur/g=Joe"
How can I get the email address in the recipient field?
My code so far:
List <string> emails = new List<string>();
if (thisMailItem.Recipients.Count > 0)
{
foreach (Recipient rec in thisMailItem.Recipients)
{
emails.Add(rec.Address);
}
}
return emails;
Can you try this ?
emails.Add(rec.AddressEntry.Address);
Reference link
EDIT:
I don't have the right environment to test so I'm just guessing all this, but how about
string email1Address = rec.AddressEntry.GetContact().Email1Address;
or .Email2Adress or .Email3Address
Also there is,
rec.AddressEntry.GetExchangeUser().Address
that you might want to try.
Try this
private string GetSMTPAddressForRecipients(Recipient recip)
{
const string PR_SMTP_ADDRESS =
"http://schemas.microsoft.com/mapi/proptag/0x39FE001E";
PropertyAccessor pa = recip.PropertyAccessor;
string smtpAddress = pa.GetProperty(PR_SMTP_ADDRESS).ToString();
return smtpAddress;
}
This is available on MSDN here
I have used the same way to get email addresses in my application and its working.
the AddressEntry also has an SMTPAddress property that exposes the primary smtp adress of the user.
I don't know if this helps or how accurate
it is, a sample
private string GetSmtp(Outlook.MailItem item)
{
try
{
if (item == null || item.Recipients == null || item.Recipients[1] == null) return "";
var outlook = new Outlook.Application();
var session = outlook.GetNamespace("MAPI");
session.Logon("", "", false, false);
var entryId = item.Recipients[1].EntryID;
string address = session.GetAddressEntryFromID(entryId).GetExchangeUser().PrimarySmtpAddress;
if (string.IsNullOrEmpty(address))
{
var rec = item.Recipients[1];
var contact = rec.AddressEntry.GetExchangeUser();
if (contact != null)
address = contact.PrimarySmtpAddress;
}
if (string.IsNullOrEmpty(address))
{
var rec = item.Recipients[1];
var contact = rec.AddressEntry.GetContact();
if (contact != null)
address = contact.Email1Address;
}
return address;
}
finally
{
}
}
I am trying to access Attachment names form "$File" (Lotus Notes).
NotesView inbox = _serverDatabase.GetView("($Inbox)");
NotesDocument docInbox = inbox.GetFirstDocument();
NotesItem file = docInbox.GetFirstItem("$File");
String fileType = file.type.ToString();
( getting fileType value "ATTACHMENT" for mail containing attachments)
I am not getting solution given in:
How to Access attachments from Notes mail?
I got solution as:
object[] items = (object[])docInbox.Items;
foreach (NotesItem nItem in items)
{
if (nItem.Name == "$FILE")
{
NotesItem file = docInbox.GetFirstItem("$File");
string fileName = ((object[])nItem.Values) [0].ToString();
NotesEmbeddedObject attachfile = (NotesEmbeddedObject)docInbox.GetAttachment(fileName);
if (attachfile != null)
{
attachfile.ExtractFile("C:\\test\\" + fileName);
}
}
But here I am getting only first attachment value.
Can anyone help me out in this?
Try something like this:
NotesView inbox = _serverDatabase.GetView("($Inbox)");
NotesDocument docInbox = inbox.GetFirstDocument();
if(docInbox.HasEmbedded ) {
foreach (NotesEmbeddedObject o in docInbox.EmbeddedObjects) {
if ( o.Type == 1454 ) {
o.ExtractFile( "c:\samples\" & o.Source )
}
}
}
Here is a link to Lotus Notes Designer Help - Really good as you can search for Classes etc to find out what options you have.
http://publib.boulder.ibm.com/infocenter/domhelp/v8r0/index.jsp?topic=/com.ibm.help.domino.designer85.doc/DOC/H_WHAT_S_NEW_IN_RNEXT_CHAP.html
Show you all the methods and properties of various class.
Hi Preeti,
OK from the other code sample you are returning an array:
string fileName = ((object[])nItem.Values) [0].ToString();
Yet you are only selecting the first value, you need to recurse through the collection.
Try something like this.
foreach (object attachment in (object[])nItem.Values)
{
NotesEmbeddedObject attachfile = (NotesEmbeddedObject)docInbox.GetAttachment(attachment.ToString());
if (attachfile != null)
{
attachfile.ExtractFile("C:\\test\\" + attachment.ToString());
}
}
Josh
Your above code snippet is very helpful to me. So, I tried the to save all attachments and finally found the below solution.
NotesView nInboxDocs = NDb.GetView("$Inbox");
NDoc=nInboxDocs.GetFirstDocument();
while (NDoc != null)
{
if (NDoc.HasEmbedded && NDoc.HasItem("$File"))
{
// To save only first attachment //
//pAttachment = ((object[])NDoc.GetItemValue("$File"))[0].ToString();
//pAttachment = CurItem.ToString();
//NDoc.GetAttachment(pAttachment).ExtractFile(#"C:\Documents and Settings\Administrator\Desktop\" + pAttachment);
// To save all attachment //
object[] AllDocItems = (object[])NDoc.Items;
foreach (object CurItem in AllDocItems)
{
NotesItem nItem = (NotesItem)CurItem;
if (IT_TYPE.ATTACHMENT == nItem.type)
{
pAttachment = ((object[])nItem.Values)[0].ToString();
NDoc.GetAttachment(pAttachment).ExtractFile(#"C:\Documents and Settings\Administrator\Desktop\" + pAttachment);
}
}
}
NDoc = nInboxDocs.GetNextDocument(NDoc);
}