I am using IMAP4 client called: MailKit.
It works great, but I have a problem on getting body of message without downloading the attachments. I want to show the mail's body text and also what attachments there are,
but only if a user clicks on the attachment I want to actually download the attachment.
I've tried:
var message = inbox.GetMessage(uid, cancel.Token);
But this gets the entire message.
Also tried:
uids[0] = uid;
var ms = inbox.Fetch(uids, MessageSummaryItems.BodyStructure , cancel.Token);
var bp1 = inbox.GetBodyPart(uid, ms.First().Body, cancel.Token);
But again this downloads the attachment.
With your sample code, you are downloading the entire message because you are requesting the top-level body part of the message.
MIME is a tree structure of "body parts". What you want to do is to traverse the ms.First().Body to find the part(s) that you want, and then download them individually using the GetBodyPart() method.
Take a look at MailKit.BodyPartMultipart, MailKit.BodyPartMessage, MailKit.BodyPartBasic and MailKit.BodyPartText.
A BodyPartMultipart contains other body parts.
A BodyPartMessage parts contains a message (which will also contain a body part).
A BodyPartBasic is a basic leaf-node body part - usually an "attachment".
A BodyPartText is a text part (a subclass of BodyPartBasic) which can either be an attached text part or what you might consider to be the main text of the message.
To figure out if a BodyPartBasic is meant to be displayed inline or as an attachment, what you need to do is:
if (part.ContentDisposition != null && part.ContentDisposition.IsAttachment)
// it is an attachment
else
// it is meant to be shown to the user as part of the message
// (if it is an image, it is meant to be displayed with the text)
I should probably add a convenience property to BodyPartBasic called IsAttachment to make this a bit simpler (I'll try to add it today).
Hope that helps.
Update: I've just added the BodyPartBasic.IsAttachment convenience property in git master, so the next release of MailKit will have it.
This IMAP command will return just the text body.
a1 uid fetch <uid> (body.peek[text])
-Rick
Related
I have a Windows Service that sends email alerts to the users, i made a folder and contains all the files and styles like a normal html.
In the properties of Email.html, i selected always copy.
To read that file in my method, i used this:
var path = AppDomain.CurrentDomain.BaseDirectory + "EmailTemplate\\Email.html";
string body = File.ReadAllText(path);
And to replace the text dinamically, just used body.Replace("#Text#", newString)
To send the email i used: MailMessage
And the final result is:
Nothing of the css imported is used, how to load all files?
Note: I edited the image with black lines
Most email clients don't support external stylesheets. You'll need to inline the CSS to get it to show up.
If you don't want to hand-convert things, MailChimp has an inline CSS converter that I've used in the past. There are other utilities/tools out there too.
I am programmatically trying to get the attachment data in C# in following way :--
Microsoft.Office.Interop.Outlook.Attachment attachment = objMail.Attachments[attachmentIndex];
if (attachment.DisplayName.Equals("Test"))
{
const string PR_ATTACH_DATA = "http://schemas.microsoft.com/mapi/proptag/0x37010102";
byte[] attachmentData = attachment.PropertyAccessor.GetProperty(PR_ATTACH_DATA);
}
Now my code is working fine if attachment is text file or image file. But if attachment is itself a mail, it throws the exception that property is unknown or can not be found.
Please suggest in which cases / type of attachments, this property "http://schemas.microsoft.com/mapi/proptag/0x37010102" will not work and in those cases, what would be alternative property / method to get the attachment data in byte array ?
Thanks
PR_ATTACH_DATA_BIN is only present for the regular by-value attachments (PR_ATTACH_METHOD == ATTACH_BY_VALUE). Embedded message or OLE attachments do not expose that property - they use PR_ATTACH_DATA_OBJ which must be opened using IAttach::OpenProperty(IID_IStorage, ...) - take a look at the existing messages using OutlookSpy - I am its author - select the message, click IMessage button, go to GetAttachmentTable tab, double click on the attachment.
Also keep in mind that PropertyAccessor.GetProperty would only be able to retrieve that property for small attachments. For large attachments, PR_ATTACH_DATA_BIN must be opened as IStream using IAttach::OpenProperty(IID_IStorage, ...) - PropertyAccessor.GetProperty does not do that. You will need to use Extended MAPI (C++ or Delphi) or Redemption (I am its author) - it exposes RDOAttachment.AsArray / AsText / AsStream properties.
Microsoft Graph Rest API is an single end point and wrapper for most Microsoft Data including events, most office products including outlook. Best of all any language can make a request to the endpoint and retrieve the information. See the complete Docs HERE to get started.
See below code for a simple Get request for outlook attachments. Note there are other more complex implementations. Docs: https://learn.microsoft.com/en-us/graph/api/attachment-get?view=graph-rest-1.0&tabs=http Scroll through the link and you can find C#, Java, and JavaScript examples on how to implement this.
GET /me/messages/{id}/attachments/{id}
GET /users/{id | userPrincipalName}/messages/{id}/attachments/{id}
GET /me/messages/{id}/attachments/{id}/$value
GET /users/{id | userPrincipalName}/messages/{id}/attachments/{id}/$value
I am using MailKit/MimeKit 1.2.7 (latest NuGet version).
I am using ImapClient to receive emails that can have diverse attachments (images, text files, binary files, etc).
MimeMessage's Attachment property helps me access all these attachments --- unless the emails are being sent with Apple Mail and contain images (it seems that Apple Mail does not attach images with Content-Disposition "attachment" (read here ... comment from Jeffrey Stedfast at the very bottom).
Embedded images are not listed in the Attachments collection.
What are my options? Do I really have to traverse the body parts one by one and see what's inside? Or is there an easier solution?
The Working with Messages document lists a few ways of examining the MIME parts within a message, but another simple option might be to use the BodyParts property on the MimeMessage.
To start, let's take a look at how the MimeMessage.Attachments property works:
public IEnumerable<MimeEntity> Attachments {
get { return BodyParts.Where (x => x.IsAttachment); }
}
As you've already noted, the reason that this property doesn't return the attachments you are looking for is because they do not have Content-Disposition: attachment which is what the MimeEntity.IsAttachment property is checking for.
An alternate rule might be to check for a filename parameter.
var attachments = message.BodyParts.Where (x => x.ContentDisposition != null && x.ContentDisposition.FileName != null).ToList ();
Or maybe you could say you just want all images:
var images = message.BodyParts.OfType<MimePart> ().Where (x => x.ContentType.IsMimeType ("image", "*")).ToList ();
Hope that gives you some ideas on how to get the items you want.
I'm writing a program where I can send mails (using domino.dll) from three different department mailboxes (each using its own mail server and nsf-file).
All three department mailboxes have a predefined mail signature such as
Regards
Department X
Since those can change anytime I don't want to hardcode the signature in my program but extract them from the mailbox/nsf-file instead and append it to the mail body (or something else if there are better approaches).
I've been looking around all day without finding a solution to this problem, so my question is: How is this achieved?
So far my code is similar to this:
public Boolean sendNotesMail(String messageText)
{
//Create new notes session
NotesSession _notesSession = new NotesSession();
//Initialize Notes Database to null;
NotesDatabase _notesDataBase = null;
//Initialize Notes Document to null;
NotesDocument _notesDocument = null;
string mailServer = #"Path/DDB";
string mailFile = #"Deparmentmail\number.nsf";
//required for send, since its byRef and not byVal, gets set later.
object oItemValue = null;
// Start the connection to Notes. Otherwise log the error and return false
try
{
//Initialize Notes Session
_notesSession.Initialize("");
}
catch
{
//Log
}
// Set database from the mailServer and mailFile
_notesDataBase = _notesSession.GetDatabase(mailServer, mailFile, false);
//If the database is not already open then open it.
if (!_notesDataBase.IsOpen)
{
_notesDataBase.Open();
}
//Create the notes document
_notesDocument = _notesDataBase.CreateDocument();
//Set document type
_notesDocument.ReplaceItemValue("Form", "Memo");
//sent notes memo fields (To and Subject)
_notesDocument.ReplaceItemValue("SendTo", emailAddress);
_notesDocument.ReplaceItemValue("Subject", subjectText);
// Needed in order to send from a departmental mailbox
_notesDocument.ReplaceItemValue("Principal", _notesDataBase.Title);
//Set the body of the email. This allows you to use the appendtext
NotesRichTextItem _richTextItem = _notesDocument.CreateRichTextItem("Body");
// Insert the text to the body
_richTextItem.AppendText(messageText);
try
{
_notesDocument.Send(false, ref oItemValue);
}
}
EDIT:
Thanks to Richard Schwartz my solution is:
object signature = _notesDataBase.GetProfileDocument("calendarprofile", "").GetItemValue("Signature");
String[] stringArray = ((IEnumerable)signature).Cast<object>().Select(x => x.ToString()).ToArray();
_richTextItem.AppendText(stringArray[0]);
The signature is stored in a profile document in the NSF file. You can use the method NotesDatabase.getProfileDocument() to access it. This method takes two arguments:
ProfileName: The profile document name that you need to find the signature is "calendarprofile". (Yes, that's right. It's actually a common profile for many functions, but the calendar developers got there first and named it. ;-))
UniqueKey: Leave this as an empty string. (It is traditionally used to store a username in profile documents in shared databases, but not used in the calendarprofile doc in the mail file.)
You access data in the profile document the same way that you access them in regular documents, e.g., using getItem(), getItemValue(), etc. For a simple text signature, the NotesItem that you are looking for is called "Signature". I notice, however, that there are also items called "Signature_1" and "Signature_2", and "SignatureOption".
If you look at the Preferences UI for setting signatures in Notes mail, you will see that there is a choice between simple text and HTML or graphic files. No doubt this choice will be reflected in the SignatureOption item, so you will probably want to check that first. I have not explored where the data goes if you use imported HTML or graphic files, so I can't say for sure whether it goes into Signature, Signature_1, Signature_2, or somewhere else. But you can explore that on your own by using NotesPeek. You can download it here. It presents a tree-style view of the NSF file. There's a branch of the tree for Profiles, and you can find the calendarprofile there. Then just play around with different settings in the Notes mail preferences and see what changes. (NotesPeek doesn't pick up changes on the fly. You have to close and re-open the profile in NotesPeek after saving changes in the Notes mail preferences dialog in order to see the changes.)
If this gets too difficult, and you want a standard solution for all mails, you might consider this product or a similar one.
I have a webpage which I would like users to be able to send to a friend at the click of a button. I am currently using Chilkat's MailMan but I keep getting intermittent problems with it. It seems occassionaly on the first attempt to mail it throws a null pointer exception. Then if try the exact same page again it sends no problem.
Are there any other components out
there that will do what I am trying
to do?
Would it be easier to right my
own light weight component to do it?
Has anyone had the above problem that
can be solved easily and then I don't
have to worry about the above?
EDIT:
Maybe I should clear something up. I know how to send emails. That is not the problem. The Chilkat component I was using could take in a webpage and put it into an email and send it. The person that receives it then has an email with all the CSS included and the pictures and everything in the email.
This is actually not a trivial exercise.
What you want to do, is download the HTML (which is the easy part). You then have to parse it, and extract all of the css references, and image references, and either:
Embed them into the email, or
Convert all links to absolute links.
When you look at all the bad HTML out there, you find out this isn't trival. The reason why I know this, is I wrote this functionality into aspNetEmail (www.aspNetEmail.com), and had to account for all sorts of bad HTML.
Could you use the WebClient class to get the webpage that the user is requesting? You'd want to change any relative links to absolute links (e.g. from "/images/logo.gif" to "http://myapp.com/images/logo.gif"), then take the output and use that as the body of the MailMessage object
i.e.
public void MailToAFriend(string friendMailAddress, Uri uriToEmail) {
MailMessage message = new MailMessage();
message.From = "your_email_address#yourserver.com";
message.To = friendEmailAddress;
message.Subject = "Check out this awesome page!";
message.Body = GetPageContents(uriToEmail);
SmtpClient mailClient = new SmtpClient();
mailClient.Send(message);
}
private string GetPageContents(Uri uri) {
var webClient = new WebClient();
string dirtyHtml = webClient.DownloadString(uri);
string cleanedHtml = MakeReadyForEmailing(dirtyHtml);
return cleanedHtml;
}
private string MakeReadyForEmailing(string html) {
// some implementation to replace any significant relative link
// with absolute links, strip javascript, etc
}
There's lots of resources on Google to get you started on the regex to do the replacement.
1) .NET comes with a reasonably adequate class for sending mail, in System.Net.Mail.
2) If it happens only rarely and does not repeat, just put it in a try block and retry two more times before considering it a failure. While it may sound crude, it's a very effective solution.