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);
}
Related
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;
}
I am currently working on the below method which iterates through emails in my Inbox, but wonder how to filter them to focus on the ones with static subject such as : nice weather. Following searches I've thought folders.Items.Restrict() could help, however I've got condition is not valid on var fi = folder.Items.Restric("weather"). I've also tried
var fi = folder.Items.Find("[Subject] = weather"), to ensure the condition is valid,... but it ends up with no improvement.
Thanks in advance
static void IterateMessages(Outlook.Folder folder)
{
string[] extensionsArray = {".csv"};
//var fi = folder.Items;
var fi = folder.Items.Restrict("weather");
if (fi != null)
{
foreach (Object item in fi)
{
Outlook.MailItem mi = (Outlook.MailItem)item;
var attachments = mi.Attachments;
if (attachments.Count != 0)
{
if (!Directory.Exists(basePath)
{
Directory.CreateDirectory(basePath);
}
// Loop through each attachment
for (int i = 1; i <= mi.Attachments.Count; i++)
{
Console.WriteLine("Processing: {0}", mi.Attachments[i].FileName);
mi.Attachments[i].SaveAsFile(basePath);
Console.WriteLine("Attachment: {0}" + " Saved to Path", mi.Attachments[i].FileName);
}
}
}
}
}
Your Restrict parameter seems to be in a wrong format. Try this:
var fi = folder.Items.Restrict("#SQL=\"urn:schemas:httpmail:subject\" like '%weather%'";
More information
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 have a task to relocate work items from one TFS project to another within one TFS collection. All history and dates have to be identical to the original item. I read a lot information and have done a lot of work. I have almost completed the task but found one crucial issue. After saving of a new item which have been copied from original one I lost my attachments in revisions. Thus I can relocate item only once, after next attempt attachments will be lost.
There is my approach to copy items:
First of all I read that article about changing creation date and changed date: TFS API Change WorkItem CreatedDate And ChangedDate To Historic Dates.
Then I had next code:
var type = project.WorkItemTypes[itemToCopy.Type.Name];
var newWorkItem = new WorkItem(type);
List<Revision> revisions = item.Revisions.Cast<Revision>().OrderBy(r => r.Index).ToList();
var firstRevision = revisions.First();
revisions.Remove(firstRevision);
SetFields(newWorkItem, firstRevision.Fields, includeAreas);
result = Save(newWorkItem);
if (result != string.Empty)
throw new Exception(result);
newWorkItem = tfsManager.GetWorkItemStore().GetWorkItem(newWorkItem.Id);
var changed = firstRevision.Fields.Cast<Field>().First(f => f.ReferenceName == "System.ChangedDate").Value;
var created = firstRevision.Fields.Cast<Field>().First(f => f.ReferenceName == "System.CreatedDate").Value;
var changedDate = (DateTime)changed;
var createdDate = (DateTime)created;
newWorkItem.Fields["System.CreatedDate"].Value = createdDate;
newWorkItem.Fields["System.ChangedDate"].Value = changedDate.AddSeconds(1);
result = Save(newWorkItem);
if (result != string.Empty)
throw new Exception(result);
var attachments = firstRevision.Attachments.Cast<Attachment>().ToList();
var attachMap = new Dictionary<int, Attachment>();
if (attachments.Count > 0)
AddAttachments(newWorkItem, firstRevision.Attachments.Cast<Attachment>().ToList(), attachMap);
else
{
result = Save(newWorkItem);
if (result != string.Empty)
throw new Exception(result);
}
ApplyRevisions(newWorkItem, revisions, attachMap, includeAreas);
return newWorkItem;
The method "SetFields" copies all editable fields from the original work item to a new work item.
The method "Save" simply saves the work item and collect all info about mistakes during save process.
The method "ApplyRevisions" simple enumerate all revisions and copy fields and attachments. It looks like:
private void ApplyRevisions(WorkItem toItem,
List<Revision> revisions, Dictionary<int,
Attachment> attachMap,
bool includeAreas)
{
foreach (var revision in revisions.OrderBy(r => r.Index))
{
SetFields(toItem, revision.Fields, includeAreas);
AddAttachments(toItem, revision.Attachments.Cast<Attachment>().ToList(), attachMap);
AddChangesetLinks(toItem, revision.Links.Cast<Link>().ToList());
var result = Save(toItem);
if (result != string.Empty)
{
SetFields(toItem, revision.Fields, includeAreas);
result = Save(toItem);
if (result != string.Empty)
{
throw new Exception(result);
}
}
}
}
And the main part is how I copy attachments:
private void AddAttachments(WorkItem item, IList<Attachment> attaches, Dictionary<int, Attachment> attachMap)
{
var guid = Guid.NewGuid();
var currentAttaches = item.Attachments.Cast<Attachment>().ToList();
var files = new List<string>();
try
{
item.Open();
foreach (var attach in attaches)
{
if (attachMap.ContainsKey(attach.Id))
{
var id = attachMap[attach.Id].Id;
if (currentAttaches.Any(a => a.Id == id))
continue;
}
var bytes = tfsManager.TfsWebClient.DownloadData(attach.Uri);
var tempFile = CreateFileName(guid, attach.Name);
files.Add(tempFile);
if (bytes != null && bytes.Length > 0)
File.WriteAllBytes(tempFile, bytes);
else
{
File.Create(tempFile).Dispose();
}
var attachInfo = new AttachmentInfo(tempFile);
attachInfo.FieldId = 50;
attachInfo.CreationDate = attach.CreationTimeUtc;
//attachInfo.LastWriteDate = attach.LastWriteTimeUtc;
attachInfo.Comment = attach.Comment;
//attachInfo.AddedDate = attach.AttachedTimeUtc;
var newAttach = Attachment.MakeAttachment(item, attachInfo);
item.Attachments.Add(newAttach);
if (!attachMap.ContainsKey(attach.Id))
attachMap[attach.Id] = newAttach;
}
foreach (var attach in currentAttaches)
{
var id = attachMap.First(pair => pair.Value.Id == attach.Id).Key;
if (attaches.All(a => a.Id != id))
{
item.Attachments.Remove(attach);
}
}
var result = Save(item);
if (result != string.Empty)
throw new Exception(result);
}
finally
{
if (files.Count > 0)
{
foreach (var file in files)
{
File.Delete(file);
}
RemoveTempFolder(guid);
}
}
}
After copying item and retrieving it from TFS property "Attachments" in property "Revisions" is empty. I guess, that the problem is in different dates... But do not know how to solve it...
Sorry for enormous amount of code. It is my first question here.
P.S.
I have digged deeper in the question and found out that the problem in AuthorizedAddedDate of AttachmentInfo. There is a comparison of this date and ChangeDate of the particular revision during of process of filling Attachments. See code below:
DateTime asof = (DateTime) changedDate;
foreach (LinkInfo linkInfo in this.m_linksData)
{
if (linkInfo.AuthorizedAddedDate <= asof && asof < linkInfo.AuthorizedRemovedDate)
yield return linkInfo;
}
ChangedDate we can change and I change it during of processing of revisions. Unfortunately, I do not know how to change AuthorizedAddedDate of AttachmentInfo... Even if I change it, the value is the same after next load of the work item... It seems that this value is prohibited for changing(
Yep, it seems that I have found the answer. At least this solution helped me. Sorry, that I have not posted it immediately. The process of adding attachment looks like that:
var attachInfo = new AttachmentInfo(tempFile);
attachInfo.AddedDate = attach.AddedDate;
attachInfo.CreationDate = attach.CreationDate;
attachInfo.Comment = string.Format(#"{0} (from attachId={1})", attach.Comment, attach.Id);
attachInfo.RemovedDate = attach.RemovedDate;
attachInfo.FieldId = 50;
itemTo.LinkData.AddLinkInfo(attachInfo, itemTo);
Where "tempFile" is a file which we want to attach (in my case it has been copied from another WorkItem) and "attach" is a container for information about AttachmentInfo. "itemTo" is a target WorkItem where we want to add attachment.
If you need more detail or you want me to clarify any moments, please, let me know.
P.S. It is important to remember about time zones when setting AddedDate, CreationDate and RemovedDate.
I need to extract/export the lotus notes email attachment into file system. for that I wrote following method but each time I am receiving an error at line foreach (NotesItem nItem in items).. Can anybody please tell me what I am doing wrong ..
Thanks
Jwalin
public void GetAttachments()
{
NotesSession session = new NotesSession();
//NotesDocument notesDoc = new NotesDocument();
session.Initialize("");
NotesDatabase NotesDb = session.GetDatabase("", "C:\\temps\\lotus\\sss11.nsf", false); //Open Notes Database
NotesView inbox = NotesDb.GetView("By _Author");
NotesDocument docInbox = inbox.GetFirstDocument();
object[] items = (object[])docInbox.Items;
**foreach (NotesItem nItem in items)**
{
//NotesItem nItem = (NotesItem)o1;
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:\\temps\\export\\" + fileName);
}
}
}
You don't need to use the $File item to get the attachment name(s). Rather, you can use the HasEmbedded and EmbeddedObject properties of the NotesDocument class.
public void GetAttachments()
{
NotesSession session = new NotesSession();
//NotesDocument notesDoc = new NotesDocument();
session.Initialize("");
NotesDatabase NotesDb = session.GetDatabase("", "C:\\temps\\lotus\\sss11.nsf", false); //Open Notes Database
NotesView inbox = NotesDb.GetView("By _Author");
NotesDocument docInbox = inbox.GetFirstDocument();
// Check if any attachments
if (docInbox.hasEmbedded)
{
NotesEmbeddedObject attachfile = (NotesEmbeddedObject)docInbox.embeddedObjects[0];
if (attachfile != null)
{
attachfile.ExtractFile("C:\\temps\\export\\" + attachfile.name);
}
}
Ed's solution didn't work for me. Something about my Notes db design seems to have left the EmbeddedObjects property null even when the HasEmbedded flag is true. So I sort of combined the Ed's and Jwalin's solutions, modifying them to fetch all attachments from a Notes document.
NotesDocument doc = viewItems.GetNthEntry(rowCount).Document;
if (doc.HasEmbedded)
{
object[] items = (object[])doc.Items;
foreach (NotesItem item in items)
{
if(item.Name.Equals("$FILE"))
{
object[] values = (object[])item.Values;
doc.GetAttachment(values[0].ToString()).ExtractFile(fileSavePath + values[0].ToString());
}
}