I have the following problem. I would like to delete attachments from emails. As long as it's a normal email with attachments, that's no problem. But if the e-mail is now in an e-mail, then I can not delete the attachments. I always get the message "at least one attachment could not be deleted".
Does somebody has any idea? I am working with version 2 of Exchange Web Services.
private void workEmail(EmailMessage rootMailMessage, EmailMessage subMailMessage, string filePath, int index)
{
EmailMessage eMessageToWork = null;
if (subMailMessage == null)
{
eMessageToWork = rootMailMessage;
}
else
{
eMessageToWork = subMailMessage;
}
for (int i = eMessageToWork.Attachments.Count; i-- > 0; )
{
Microsoft.Exchange.WebServices.Data.Attachment rootAttachment = eMessageToWork.Attachments[i];
if (rootAttachment is FileAttachment)
{
// For now, just .odt files are not supported and it throws an exception if theres any unsupported fileextension
checkFileTypeSupported(rootAttachment.Name);
string strType = Path.GetExtension(rootAttachment.Name);
// check if it is any type of supported image or pdf file
if (checkForImageOrPdfAttachment(strType))
{
// just save the image to temp folder
string subAttRootFileName = saveImageFileAttachment(rootAttachment, index, filePath);
// remove attachment
eMessageToWork.Attachments.Remove(rootAttachment);
// save the updated mail
rootMailMessage.Update(ConflictResolutionMode.AlwaysOverwrite);
}
continue;
}
else // Attachment is an item attachment.
{
// convert attachment to itemattachment
ItemAttachment itmAttach = rootAttachment as ItemAttachment;
// save this item-attachment
// Load Item with additionalProperties of MimeContent
itmAttach.Load(EmailMessageSchema.MimeContent);
// convert the itemattachment to a emailmessage
EmailMessage ebMessage = itmAttach.Item as EmailMessage;
// recursive call for possible attachments in this emailmessage
this.workEmail(rootMailMessage, ebMessage, filePath, index + 1);
// remove the attached mailitem from parent mail
rootMailMessage.Attachments.Remove(rootAttachment);
// update parent mail
rootMailMessage.Update(ConflictResolutionMode.AlwaysOverwrite);
}
}
}
Try this:
EmailMessage message = EmailMessage.Bind(service, Id, new PropertySet(ItemSchema.Attachments));
foreach (Attachment attachment in message.Attachments)
{
message.Attachments.Remove(attachment);
break;
}
message.Update(ConflictResolutionMode.AlwaysOverwrite);
Related
foreach (var part in emailInfoResponse.Result.Payload.Parts)
{
if (part.Parts != null)
foreach (var innerPart in part.Parts)
{
if (innerPart.MimeType == "text/plain")
{
body = innerPart.Body.Data;
}
}
}
I am successfully reading the body ONLY of the main mail. It is not taking the data from the reply messages assigned to the mail,
any ideas how I can I read the replies to the mail too? Or even if there is a way to take only the replies of a specific mail
To get later / other messages in a conversation, you need to retrieve the source thread:
String threadID = emailInfoResponse.Result.ThreadId;
Thread thread = service.Users.Threads.Get(userID, threadID).Execute();
foreach( var msg in thread.Messages )
{
/* your code for each message */
}
Per the Gmail message reference, messages can share a threadId if they have both the correct headers and the same subject. Otherwise, they will be on different email threads.
You'd probably want to refactor a bit and start from a ThreadList. This way, you can find all conversations that match a specific input query (to find only the messages that correspond to these client lists):
Make data storage object: Dictionary<String, Container>, where the string is the company (from the email subject, or wherever), and the container probably only needs unique values so HashSet or similar.
Page through the ThreadList
For each thread
Page through messages
For each message, if the first message, get the subject i.e. company name (all messages have to share a subject in the same thread).
Then append the clients to the data storage object based on the subject (company name).
Construct your database records from the data storage object as appropriate.
public static MimeKit.MimeMessage Reply(MimeKit.MimeMessage message, bool replyToAll)
{
var reply = new MimeKit.MimeMessage();
if (message.ReplyTo.Count > 0)
{
reply.To.AddRange(message.ReplyTo);
}
else if (message.From.Count > 0)
{
reply.To.AddRange(message.From);
}
else if (message.Sender != null)
{
reply.To.Add(message.Sender);
}
if (replyToAll)
{
reply.To.AddRange(message.To);
reply.Cc.AddRange(message.Cc);
}
if (!message.Subject.StartsWith("Re:", StringComparison.OrdinalIgnoreCase))
reply.Subject = "Re:" + message.Subject;
else
reply.Subject = message.Subject;
if (!string.IsNullOrEmpty(message.MessageId))
{
reply.InReplyTo = message.MessageId;
foreach (var id in message.References)
reply.References.Add(id);
reply.References.Add(message.MessageId);
}
using (var quoted = new StringWriter())
{
var sender = message.Sender ?? message.From.Mailboxes.FirstOrDefault();
quoted.WriteLine("On {0}, {1} wrote:", message.Date.ToString("f"), !string.IsNullOrEmpty(sender.Name) ? sender.Name : sender.Address);
using (var reader = new StringReader(message.TextBody))
{
string line;
while ((line = reader.ReadLine()) != null)
{
quoted.Write("> ");
quoted.WriteLine(line);
}
}
reply.Body = new MimeKit.TextPart("plain")
{
Text = quoted.ToString()
};
}
return reply;
}
I'm developing application which uses EWS managed API to send appointments to outlook recipients,
Now there are requirement to add attachments to appointment, I'm able to attach attachments to emails, but when I use same technique as attaching item attachments to email ,but attachments are not attaching, my code as below
public string sendCalanderEvntAsReply( EntityLayer.Data_Contracts.AppointmentDTO appointment)
{
Appointment app = new Appointment(service);
app.Subject = appointment.Subject;
app.Body = appointment.Body;
app.Start = Convert.ToDateTime(appointment.Start);
app.End = Convert.ToDateTime(appointment.End);
app.Location = appointment.Location;
foreach (string obj in appointment.Attendees)
{
app.RequiredAttendees.Add(obj);
}
if (appointment.Attachments != null &&
appointment.Attachments.Count > 0)
{
foreach (var att in appointment.Attachments)
{
app.Attachments.AddFileAttachment(att.FileName);
}
}
app.Save(SendInvitationsMode.SendToAllAndSaveCopy);
}
is there any issue in my code?
please help.
thanks
With EWS when you want to send an Attachment with the Meeting invitation you need to save the appointment first before you send the message else you will only get the attachment on the owners copy so with your code you should use something like
Appointment app = new Appointment(service);
app.Subject = appointment.Subject;
app.Body = appointment.Body;
app.Start = Convert.ToDateTime(appointment.Start);
app.End = Convert.ToDateTime(appointment.End);
app.Location = appointment.Location;
if (appointment.Attachments != null &&
appointment.Attachments.Count > 0)
{
foreach (var att in appointment.Attachments)
{
app.Attachments.AddFileAttachment(att.FileName);
}
}
app.Save(SendInvitationsMode.SendToNone);
foreach (string obj in appointment.Attendees)
{
app.RequiredAttendees.Add(obj);
}
app.Update(ConflictResolutionMode.AutoResolve, SendInvitationsOrCancellationsMode.SendToAllAndSaveCopy);
I have a system that sends emails with inline pictures. The problem is how Outlook 2013 displays the attachments. Can I update my code in a way that tells outlook not to display the paperclip icon seen here?
The idea is that I only want to display this icon when full sized pictures are attached. Not inline attachments.
Here's the code that generates the email. Create a basic console app, specify your To / mailserver / picture path, and run.
static void Main(string[] args)
{
Console.WriteLine("Prepping email message....");
var subject = "Test Subject With Inline";
var message = "<p>This is a test message.</p><br/><br/><p>[CompanyLogo]</p>";
var to = new List<string>();
to.Add("My.Name#company.com");
Console.WriteLine("Sending email message....");
if (SendMessageToFrom(subject, message, to, new List<string>()))
{
Console.WriteLine("Email sent! Check your inbox.");
}
else
{
Console.WriteLine("Error sending email!");
}
}
public static bool SendMessageToFrom(String subject, String message, List<String> to, List<String> cc)
{
try
{
// Construct the email
var sendMessage = new MailMessage()
{
IsBodyHtml = true,
From = new MailAddress("noreply#company.com"),
Subject = subject,
Body = message
};
if (sendMessage.Body.Contains("[CompanyLogo]"))
{
sendMessage.AlternateViews.Add(EmbedLogo(sendMessage.Body));
}
// Add the list of recipients
foreach (var recipient in to)
{
sendMessage.To.Add(recipient);
}
foreach (var recipient in cc)
{
sendMessage.CC.Add(recipient);
}
//Specify the SMTP server
var smtpServerName = "mailserver.company.com";
var mailClient = new SmtpClient(smtpServerName);
mailClient.Send(sendMessage);
return true;
}
catch
{
throw;
}
}
private static AlternateView EmbedLogo(string html)
{
var inline = new LinkedResource("img\\company-logo.jpg");
inline.ContentId = Guid.NewGuid().ToString();
html = html.Replace("[CompanyLogo]", string.Format(#"<img src='cid:{0}'/>", inline.ContentId));
var result = AlternateView.CreateAlternateViewFromString(html, null, System.Net.Mime.MediaTypeNames.Text.Html);
result.LinkedResources.Add(inline);
return result;
}
Update: Here's the code that did the trick:
private static MailMessage EmbedLogo(MailMessage mail)
{
var inline = new Attachment("img\\company-logo.jpg");
inline.ContentId = Guid.NewGuid().ToString();
inline.ContentDisposition.Inline = true;
inline.ContentDisposition.DispositionType = DispositionTypeNames.Inline;
mail.Body = mail.Body.Replace("[CompanyLogo]", string.Format(#"<img src='cid:{0}'/>", inline.ContentId));
mail.Attachments.Add(inline);
return mail;
}
And I also updated the main method to this:
if (sendMessage.Body.Contains("[CompanyLogo]"))
{
sendMessage = EmbedLogo(sendMessage);
}
Make sure your attachments have the Content-ID MIME header and the message's HTML body refers to them using the cid attribute : <img src="cid:xyz"> (where xyz is the value of the Content-ID MIME header).
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 created a Web Email Application, How do I view and save attached files?
I am using OpenPop, a third Party dll, I can send emails with attachments and read emails with no attachments.
This works fine:
Pop3Client pop3Client = (Pop3Client)Session["Pop3Client"]; // Creating newPopClient
int messageNumber = int.Parse(Request.QueryString["MessageNumber"]);
Message message = pop3Client.GetMessage(messageNumber);
MessagePart messagePart = message.MessagePart.MessageParts[1];
lblFrom.Text = message.Headers.From.Address; // Writeing message.
lblSubject.Text = message.Headers.Subject;
lblBody.Text=messagePart.BodyEncoding.GetString(messagePart.Body);
This second portion of code displays the contents of the attachment, but that's only useful if its a text file. I need to be able to save the attachment. Also the bottom section of code I have here over writes the body of my message, so if I receive an attachment I can't view my message body.
if (messagePart.IsAttachment == true) {
foreach (MessagePart attachment in message.FindAllAttachments()) {
if (attachment.FileName.Equals("blabla.pdf")) { // Save the raw bytes to a file
File.WriteAllBytes(attachment.FileName, attachment.Body); //overwrites MessagePart.Body with attachment
}
}
}
If anyone is still looking for answer this worked fine for me.
var client = new Pop3Client();
try
{
client.Connect("MailServerName", Port_Number, UseSSL); //UseSSL true or false
client.Authenticate("UserID", "password");
var messageCount = client.GetMessageCount();
var Messages = new List<Message>(messageCount);
for (int i = 0;i < messageCount; i++)
{
Message getMessage = client.GetMessage(i + 1);
Messages.Add(getMessage);
}
foreach (Message msg in Messages)
{
foreach (var attachment in msg.FindAllAttachments())
{
string filePath = Path.Combine(#"C:\Attachment", attachment.FileName);
if(attachment.FileName.Equals("blabla.pdf"))
{
FileStream Stream = new FileStream(filePath, FileMode.Create);
BinaryWriter BinaryStream = new BinaryWriter(Stream);
BinaryStream.Write(attachment.Body);
BinaryStream.Close();
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("", ex.Message);
}
finally
{
if (client.Connected)
client.Dispose();
}
for future readers there is easier way with newer releases of Pop3
using( OpenPop.Pop3.Pop3Client client = new Pop3Client())
{
client.Connect("in.mail.Your.Mailserver.com", 110, false);
client.Authenticate("usernamePop3", "passwordPop3", AuthenticationMethod.UsernameAndPassword);
if (client.Connected)
{
int messageCount = client.GetMessageCount();
List<Message> allMessages = new List<Message>(messageCount);
for (int i = messageCount; i > 0; i--)
{
allMessages.Add(client.GetMessage(i));
}
foreach (Message msg in allMessages)
{
var att = msg.FindAllAttachments();
foreach (var ado in att)
{
ado.Save(new System.IO.FileInfo(System.IO.Path.Combine("c:\\xlsx", ado.FileName)));
}
}
}
}
The OpenPop.Mime.Message class has ToMailMessage() method that converts OpenPop's Message to System.Net.Mail.MailMessage, which has an Attachments property. Try extracting attachments from there.
I wrote this quite a long time ago, but have a look at this block of code that I used for saving XML attachments within email messages sat on a POP server:
OpenPOP.POP3.POPClient client = new POPClient("pop.yourserver.co.uk", 110, "your#email.co.uk", "password_goes_here", AuthenticationMethod.USERPASS);
if (client.Connected) {
int msgCount = client.GetMessageCount();
/* Cycle through messages */
for (int x = 0; x < msgCount; x++)
{
OpenPOP.MIMEParser.Message msg = client.GetMessage(x, false);
if (msg != null) {
for (int y = 0; y < msg.AttachmentCount; y++)
{
Attachment attachment = (Attachment)msg.Attachments[y];
if (string.Compare(attachment.ContentType, "text/xml") == 0)
{
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
string xml = attachment.DecodeAsText();
doc.LoadXml(xml);
doc.Save(#"C:\POP3Temp\test.xml");
}
}
}
}
}
List<Message> lstMessages = FetchAllMessages("pop.mail-server.com", 995, true,"Your Email ID", "Your Password");
The above line of code gets the list of all the messages from your email using corresponding pop mail-server.
For example, to get the attachment of latest (or first) email in the list, you can write following piece of code.
List<MessagePart> lstAttachments = lstMessages[0].FindAllAttachments(); //Gets all the attachments associated with latest (or first) email from the list.
for (int attachment = 0; attachment < lstAttachments.Count; attachment++)
{
FileInfo file = new FileInfo("Some File Name");
lstAttachments[attachment].Save(file);
}
private KeyValuePair<byte[], FileInfo> parse(MessagePart part)
{
var _steam = new MemoryStream();
part.Save(_steam);
//...
var _info = new FileInfo(part.FileName);
return new KeyValuePair<byte[], FileInfo>(_steam.ToArray(), _info);
}
//... How to use
var _attachments = message
.FindAllAttachments()
.Select(a => parse(a))
;
Just in case someone wants the code for VB.NET:
For Each emailAttachment In client.GetMessage(count).FindAllAttachments
AttachmentName = emailAttachment.FileName
'----// Write the file to the folder in the following format: <UniqueID> followed by two underscores followed by the <AttachmentName>
Dim strmFile As New FileStream(Path.Combine("C:\Test\Attachments", EmailUniqueID & "__" & AttachmentName), FileMode.Create)
Dim BinaryStream = New BinaryWriter(strmFile)
BinaryStream.Write(emailAttachment.Body)
BinaryStream.Close()
Next