This is probably very simple but I am extremely new to coding anything, sorry in advance.
Currently I have a button4 that will read through my inbox for messages with a certain subject, if condition is met it displays the messages first class properties in a listview but I want it to also download the link found in each email.
It is a .zip link that when the link is clicked from inside the email it will download the zip. I want it to automatically download all links found when button4 is clicked.
I will show my button4 code and then an example of what the email is.
button4 code:
private void button4_Click(object sender, EventArgs e)
{
EmailConnect();
TimeSpan ts = new TimeSpan(0, -2, 0, 0);
DateTime date = DateTime.Now.Add(ts);
SearchFilter.IsGreaterThanOrEqualTo filter = new SearchFilter.IsGreaterThanOrEqualTo(ItemSchema.DateTimeReceived, date);
if (service != null)
{
FindItemsResults<Item> findResults = service.FindItems(WellKnownFolderName.Inbox, filter, new ItemView(50));
foreach (Item item in findResults)
{
EmailMessage message = EmailMessage.Bind(service, item.Id);
string subject = message.Subject.ToString();
if (subject.Contains("NFIRS File Validation"))
{
ListViewItem listitem = new ListViewItem(new[]
{message.DateTimeReceived.ToString(), message.From.Name.ToString() + "(" + message.From.Address.ToString() + ")", message.Subject, ((message.HasAttachments) ? "Yes" : "No")});
lstMsg.Items.Add(listitem);
}
}
if (findResults.Items.Count <= 0)
{
lstMsg.Items.Add("No Messages found!!");
}
}
}
Example email:
NFIRS File Validation
The NFIRS File Validation service has completed processing your files. Please follow this link to retrieve the zip file containing your results.
https://www.nfirs.fema.gov/biarchive/xxxxxxxxx_xxxxxxxxx.zip
This file will be deleted after 28 days.
If you have any questions, please do not reply to this email. Instead, please contact the NFIRS Support Center.
This is basically a duplicate of the link #DonBoitnott commented the only extra steps I am taking is putting the body of each email into a property list parsing it and making sure it saves as the same filename as the URL had in the original email
private void handleLinks(List<EmailProperties> properties)
{
using (WebClient client = new WebClient())
{
foreach (var prop in properties)
{
string link = searchForLink(prop.Body);
string fileName = MyExtensions.Between(link, "https://www.nfirs.fema.gov/biarchive/", ".zip");
string saveTo = string.Format((#"C:\Users\Foo\Downloads\{0}.zip"), fileName);
prop.Name = fileName;
client.DownloadFile(link, saveTo);
}
}
}
private string searchForLink(string body)
{
return MyExtensions.Between(body, "results.\r\n\r\n", "\r\n\r\nThis file will");
}
Related
Currently my application has ability to process incoming emails and bounce then back if it does not match with given criteria in code. However, I want to add up another type of email to Process which are NDR "Non-Delivery Reports" from Microsoft Exchange Server. So my application do not responsd/Bounce back NDR to exchange server which cause a loop between my Mailbox and Exchange Server.
Following method triggers when Invalid doesn't have a specific
private static void ProcessInvidMsgWithoutNo(string sMsgFrom, string sFromEmail, EmailMsg sMsgReceived, EmailMessage message)
{
EmailMsg.MoveToInvalid(message);
sMsgReceived.IsValid = false;
SaveMsgReceived(sMsgReceived, 0, string.Empty);
if (!sFromEmail.Equals(string.Empty))
{
ResponseForInvidMsg(sFromEmail);
}
else
{
curLog.WriteLog(string.Format(CultureInfo.CurrentCulture, MsgEMChecker27, sMsgFrom));
}
}
Following Method triggers to respond incoming Invalid message as stated above.
private static void ResponseForInvidMsg(string sFromEmail)
{
string tErrSubjectMsg = String.Format(CultureInfo.CurrentCulture, "{0}\\Resource\\MsgErrorSubjectAck.html", Entity.GetSetting("DocRootDir"));
StringBuilder htmlText = new StringBuilder();
FileStream fsFile = new FileStream(tErrSubjectMsg, FileMode.Open);
if (fsFile != null)
{
StreamReader reader = new StreamReader(fsFile, Encoding.Default);
string text;
do
{
text = reader.ReadLine();
if ((text != null) && (text != ""))
htmlText.Append(text + "\n");
} while (text != null);
reader.Close();
fsFile.Close();
fsFile = null;
}
else
htmlText.Append("hello");
string tToCustomerSubject = ReplyForInvalid;
string tMessage = htmlText.ToString();
EmailMsg emTo = new EmailMsg(string.Empty, sFromEmail, tToCustomerSubject, tMessage);
emTo.MsgType = EmailMsg.TypeSentCustomer;
emTo.Send(false); //Not save but CC to generic email box
}
Please, help me to find a way where I can stop my code to respond Exchange Server NDR. Thanks
The place to start would be to check the ItemClass of the message see https://learn.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-asemail/51d84da6-a2da-41e9-8ca7-eb6c4e72c28d . NDR's, delivery report etc should have a prefix of Report eg
REPORT.IPM.NOTE.NDR Non-delivery report for a standard message.
REPORT.IPM.NOTE.DR Delivery receipt for a standard message.
I'm currently writing a Windows Service to log in to a specific Exchange account, get any new emails, parse them, and save the email in the appropriate folder.
Everything is working perfectly, except saving the email.
Relevant code blocks (try / catch blocks and irrelevant stuff removed to keep it short):-
Set up the Service
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
service.Credentials = new WebCredentials(emailAddress, password);
service.AutodiscoverUrl(emailAddress, RedirectionUrlValidationCallback);
CheckForNewEmails(service);
Getting and checking new emails
private static void CheckForNewEmails(ExchangeService service)
{
int offset = 0;
int pageSize = 50;
bool more = true;
ItemView view = new ItemView(pageSize, offset, OffsetBasePoint.Beginning);
view.PropertySet = PropertySet.IdOnly;
FindItemsResults<Item> findResults;
List<EmailMessage> emails = new List<EmailMessage>();
while (more)
{
findResults = service.FindItems(WellKnownFolderName.Inbox, view);
foreach (var item in findResults.Items)
{
emails.Add((EmailMessage)item);
}
more = findResults.MoreAvailable;
if (more)
{
view.Offset += pageSize;
}
}
if (emails.Count > 0)
{
PropertySet properties = (BasePropertySet.FirstClassProperties);
service.LoadPropertiesForItems(emails, properties);
var mailItems = new List<DatabaseService.MailItem>();
var dbService = new DatabaseService();
var defaultUser = dbService.GetDefaultUser(defaultUserID);
foreach (var email in emails)
{
var mailItem = new DatabaseService.MailItem();
mailItem.mail = email;
mailItem.MessageID = email.InternetMessageId;
mailItem.Sender = email.Sender.Address;
dbService.FindLinks(service, ref mailItem, defaultUser);
mailItems.Add(mailItem);
LogMessage += (string.Format("Message ID : {1}{0}Sent : {2}{0}From : {3}{0}Subject : {4}{0}Hash : {5}{0}{6}{0}{0}", Environment.NewLine,
mailItem.MessageID ,
email.DateTimeSent.ToString("dd/MM/yyyy hh:mm:ss"),
email.Sender,
email.Subject,
mailItem.Hash,
mailItem.LinkString
));
}
}
}
Finding who it should be linked to
public void FindLinks(ExchangeService service, ref MailItem mailItem, User defaultUser)
{
string address = mailItem.Sender;
// get file hash
var tempPath = Path.GetTempPath();
var fileName = GetFilenameFromSubject(mailItem);
var fullName = Path.Combine(tempPath, fileName);
SaveAsEML(service, mailItem, fullName);
var sha = new SHA256Managed();
mailItem.Hash = Convert.ToBase64String(sha.ComputeHash(File.OpenRead(fullName)));
File.Delete(fullName);
using (var db = DatabaseHelpers.GetEntityModel())
{
// Do all the linking stuff
}
}
And finally, the problem area: Saving the file to disk (in this case to a temporary folder so I can get the file hash, to check it's not a duplicate (e.g. CC'd etc))
Looking through StackOverflow and various other sources, there seem to be two ways to do this:-
1) Using the MailItem.Load method
private string SaveAsEML(ExchangeService service, MailItem mailItem, string savePath)
{
using (FileStream fileStream = File.Open(savePath, FileMode.Create, FileAccess.Write))
{
mailItem.mail.Load(new PropertySet(ItemSchema.MimeContent));
fileStream.Write(mailItem.mail.MimeContent.Content, 0, mailItem.mail.MimeContent.Content.Length);
}
}
Using the above code, the file is created and has the correct content.
However, attempting to access any of the email properties AFTER this point results in a Null Exception crash (big crash too, no Try/Catch or UnhandledException trap will pick it up, just kills the Service)
In the above code, this line crashes the entire service:-
LogMessage += (string.Format("Message ID : {1}{0}Sent : {2}{0}From : {3}{0}Subject : {4}{0}Hash : {5}{0}{6}{0}{0}", Environment.NewLine,
mailItem.MessageID ,
email.DateTimeSent.ToString("dd/MM/yyyy hh:mm:ss"),
email.Sender,
email.Subject,
mailItem.Hash,
mailItem.LinkString
));
Specifically, referencing email.DateTimeSent
I added in some diagnostic code directly before this line:-
if (email == null) { Log it's null; } else { Log NOT null;}
if (email.DateTimeSent == null) { Log it's null; } else { Log NOT null; }
The first line logs NOT null, so email still exists.
However, the second line instantly crashes with a null exception error, without logging anything.
If I comment out the line SaveAsEML(service, mailItem, fullName); from FindLinks then everything works perfectly (except of course the file isn't saved).
2) Binding a Property Set
private string SaveAsEML(ExchangeService service, MailItem mailItem, string savePath)
{
using (FileStream fileStream = File.Open(savePath, FileMode.Create, FileAccess.Write))
{
PropertySet props = new PropertySet(EmailMessageSchema.MimeContent);
var email = EmailMessage.Bind(service, mailItem.mail.Id, props);
fileStream.Write(mailItem.mail.MimeContent.Content, 0, mailItem.mail.MimeContent.Content.Length);
}
}
Doing it this way, nothing crashes, it goes through every email just fine, and can reference email.DateTimeSent and all the other properties.
Unfortunately, it creates zero-length files, no content.
Been banging my head against the wall for hours now over this (took me an hour of adding diagnostics everywhere just to track down the crash happening when referencing properties), and it's undoubtedly something trivial I've overlooked, so if some kind soul could point out my stupidity I'd be most grateful!
Edit:
I was able to work around the above problem easily enough by simply saving the value of the properties before using them:-
foreach (var email in emails)
{
var dateTimeSent = email.DateTimeSent;
var sender = email.Sender;
var subject = email.Subject;
var mailItem = new DatabaseService.MailItem();
mailItem.mail = email;
mailItem.MessageID = email.InternetMessageId;
mailItem.Sender = email.Sender.Address;
dbService.FindLinks(service, ref mailItem, defaultUser);
mailItems.Add(mailItem);
LogMessage += (string.Format("Message ID : {1}{0}Sent : {2}{0}From : {3}{0}Subject : {4}{0}Hash : {5}{0}{6}{0}{0}", Environment.NewLine,
mailItem.MessageID,
dateTimeSent.ToString("dd/MM/yyyy hh:mm:ss"),
sender,
subject,
mailItem.Hash ?? "No Hash",
mailItem.LinkString ?? "No Linkstring"
));
}
This allowed me to use the MailItem.Load method which saved the file, and thus do what I was after in this specific case.
However there are other things this Service will need to do, and I really don't want to have to save a copy of every single property I'll need to access.
The reason this would fail
private string SaveAsEML(ExchangeService service, MailItem mailItem, string savePath)
{
using (FileStream fileStream = File.Open(savePath, FileMode.Create, FileAccess.Write))
{
PropertySet props = new PropertySet(EmailMessageSchema.MimeContent);
var email = EmailMessage.Bind(service, mailItem.mail.Id, props);
fileStream.Write(mailItem.mail.MimeContent.Content, 0, mailItem.mail.MimeContent.Content.Length);
}
}
Is that you have used Bind to Load the message with the MimeContent in the email variable and then you haven't used that. mailItem.mail won't be linked at all to email variable created as part of this operation (even though they are the same object on the server). Locally these are just two separate variables. EWS is client/server so you make a request and the Managed API will return a local object that represents the result of the operation. But that object is disconnected hence when you do the bind above it just generates another client object to represent the result of that operation. eg so the above should have been
private string SaveAsEML(ExchangeService service, MailItem mailItem, string savePath)
{
using (FileStream fileStream = File.Open(savePath, FileMode.Create, FileAccess.Write))
{
PropertySet props = new PropertySet(EmailMessageSchema.MimeContent);
var email = EmailMessage.Bind(service, mailItem.mail.Id, props);
fileStream.Write(email.MimeContent.Content, 0, email .MimeContent.Content.Length);
}
}
I am trying to create Outlook Addin using C# by Customizing Application_ItemSend event of the Send button.
I need All the email details before sending an email.
When i run the below code # my home i get proper results by putting some personal email id its working.
But when i run this similar code in office outlook machine i get the names.
As by default outlook's check names code is enabled, this returns first and last name.
I am using Outlook 2010 # both the places. Office outlook is mapped to office active directory. my home outlook is not mapped. Can anyone provide a common solution which will give me all the email address used(to, cc, bcc & from) irrespective of active directory mapped or not.
private void ThisAddIn_Startup(object sender, System.EventArgs e) {
Application.ItemSend += new Outlook.ApplicationEvents_11_ItemSendEventHandler(Application_ItemSend);
}
void Application_ItemSend(object Item, ref bool Cancel) {
Outlook.MailItem mail = Item as Outlook.MailItem;
Outlook.Inspector inspector = Item as Outlook.Inspector;
System.Windows.Forms.MessageBox.Show(mail.CC);
System.Windows.Forms.MessageBox.Show(mail.BCC);
}
Maybe is late, but someone can check this code (it's works for me)
private string[] GetCCBCCFromEmail(Outlook.MailItem email)
{
string[] ccBCC = new string[] { "", "" };//cc y bcc
Outlook.Recipients recipients = email.Recipients;
foreach (Outlook.Recipient item in recipients)
{
switch (item.Type)
{
case (int)Outlook.OlMailRecipientType.olCC:
ccBCC[0] += GetEmail(item.AddressEntry) + ";";
break;
case (int)Outlook.OlMailRecipientType.olBCC:
ccBCC[1] += GetEmail(item.AddressEntry) + ";";
break;
}
}
return ccBCC;
}
private string GetEmail(Outlook.AddressEntry address)
{
string addressStr = "";
if (address.AddressEntryUserType ==
Outlook.OlAddressEntryUserType.
olExchangeUserAddressEntry
|| address.AddressEntryUserType ==
Outlook.OlAddressEntryUserType.
olExchangeRemoteUserAddressEntry)
{
//Use the ExchangeUser object PrimarySMTPAddress
Outlook.ExchangeUser exchUser =
address.GetExchangeUser();
if (exchUser != null)
{
addressStr = exchUser.PrimarySmtpAddress;
}
}
//Get the address from externals
if (address.AddressEntryUserType == Outlook.OlAddressEntryUserType.
olSmtpAddressEntry)
{
addressStr = address.Address;
}
return addressStr;
}
Hope it helps
To/CC/BCC properties (corresponding to PR_DISPLAY_TO/CC/BCC in MAPI) are updated by the store provider when the item is saved (MailItem.Save). You can also access all recipients using the MailItem.Recipeints collection.
I want to move/copy my Email Attachments to new folder in outlook ;/ and my code doesn't work properly.
foreach(Item item in findResults.Items)
{
EmailMessage email = EmailMessage.Bind(service, item.Id, new PropertySet(BasePropertySet.FirstClassProperties, ItemSchema.Attachments));
if(false)
{
// OTC Marker HTML Body
}
else
{
if (email.HasAttachments)
{
foreach (Attachment attachment in email.Attachments)
{
EmailMessage emailAttachment = EmailMessage.Bind(service, attachment.Id, new PropertySet(BasePropertySet.FirstClassProperties, ItemSchema.Attachments));
ItemAttachment itemAttachment = attachment as ItemAttachment;
itemAttachment.Load();
EmailMessage mess = itemAttachment.Item as EmailMessage;
moveToTestFolder (mess, #"TestFolder");
}
}
else
{
//to do
}
}
}
And my moveToTestFolder method:
private void moveToTestFolder (EmailMessage item, string folderName)
{
Folder rootfolder = Folder.Bind(service, WellKnownFolderName.MsgFolderRoot);
rootfolder.Load();
var folders = rootfolder.FindFolders(new FolderView(20));
var folderItemToMove = folders.FirstOrDefault(f => f.DisplayName.Equals(folderName, StringComparison.OrdinalIgnoreCase));
item.Move(folderItemToMove.Id);
}
I'am trying to move attachment (if it is an email) to special folder in outlook. Moving the normal message is working now.
That won't work because you can only use the Move and Copy operations to copy an actual Mailbox Item not Attachments (you should be getting an error about and Invalid Id). One workaround for this is to get the MimeContent for the Email Attachment you want to move and then create a New object from that MimeCotent and save it to the folder you want to move the Item to eg
foreach (Attachment Attach in EWSItem.Attachments)
{
if (Attach is ItemAttachment)
{
PropertySet psProp = new PropertySet(BasePropertySet.FirstClassProperties);
psProp.Add(ItemSchema.MimeContent);
((ItemAttachment)Attach).Load(psProp);
if (((ItemAttachment)Attach).Item.MimeContent != null)
{
EmailMessage NewMessage = new EmailMessage(service);
NewMessage.MimeContent = ((ItemAttachment)Attach).Item.MimeContent;
NewMessage.SetExtendedProperty(new ExtendedPropertyDefinition(3591, MapiPropertyType.Integer), "1");
NewMessage.Save(folderItemToMove.Id);
}
}
}
You don't get full Fidelity of all the Exchange properties on the Message with this method as only the MimeContent is copied which is generally not a problem with Email but will be an issue for other objects types like Contacts, Tasks etc.
Cheers
Glen
so I have a script that essentially iterates through a bunch of delimited text files and uploads the images from said files to a SharePoint site. It works great, expect with one minor problem, I have a couple of images that are >4MB in size and these give me a (400) Bad Request error when the script attempts to upload them.
Code below:
class spImageUpload()
{
private static System.Collections.Generic.List<string> keywords;
private static NetworkCredential credentials = new NetworkCredential(username, password, domain);
private static ClientContext clientContext = new ClientContext(site name);
private static Web site = clientContext.Web;
private static List list = site.Lists.GetByTitle(listName);
private static FileCreationInformation newFile = new FileCreationInformation();
private static Image image = new Image();
private static FileIO fo = new FileIO();
public SharePointAccess()
{
sharepointLogin();
uploadImage();
}
private static void updateFields()
{
//Loads the site list
clientContext.Load(list);
//Creates a ListItemCollection object from list
ListItemCollection listItems = list.GetItems(CamlQuery.CreateAllItemsQuery());
//Loads the listItems
clientContext.Load(listItems);
//Executes the previous queries on the server
clientContext.ExecuteQuery();
//For each listItem...
foreach (var listItem in listItems)
{
//Writes out the item ID and Title
//Console.WriteLine("Id: {0} Title: {1}", listItem.Id, listItem["Title"]);
//Loads the files from the listItem
clientContext.Load(listItem.File);
//Executes the previous query
clientContext.ExecuteQuery();
//Writes out the listItem File Name
//Console.WriteLine("listItem File Name: {0}", listItem.File.Name);
//Looks for the most recently uploaded file, if found...
if (listItem.File.Name.Contains(fileName))
{
title = fileName;
//Changes the Title field value
listItem["Title"] = title;
//Changes the Keywords field value using the keywords list
foreach (var keyword in keywords)
{
listItem["Keywords"] += keyword;
//Writes out the item ID, Title, and Keywords
//Console.WriteLine("Id: {0} Title: {1} Keywords: {2}", listItem.Id, listItem["Title"], listItem["Keywords"]);
}
}
//Remember changes...
listItem.Update();
}
//Executes the previous query and ensures changes are committed to the server
clientContext.ExecuteQuery();
}
private static void uploadImage()
{
try
{
fo.loadFile();
foreach (var img in fo.lImageSet)
{
Console.WriteLine("Image Name: {0}", img.getName());
}
foreach (var img in fo.lImageSet)
{
DateTime start;
DateTime end;
start = DateTime.Now;
//Sets file path equal to the path value stored in the current image of lImageSet
filePath = img.getPath();
//Writes out to the console indicating what's been stored in filePath
Console.WriteLine("Image Path: {0}", filePath);
//Reads in the contents of the file
newFile.Content = System.IO.File.ReadAllBytes(filePath);
//Sets the file name equal to the name value stored in the current image of lImageSet
fileName = img.getName() + ".jpeg";
//Sets the URL path for the file
newFile.Url = fileName;
//Creates a List object of type String
keywords = new System.Collections.Generic.List<string>();
//For each keyword in the current image stored in lImageSet...
foreach (var keyword in img.lTags)
{
//...add that keyword to the newly created list
keywords.Add(keyword);
}
//Uploads the file to the picture library
Microsoft.SharePoint.Client.File uploadFile = list.RootFolder.Files.Add(newFile);
//Loads uploadFile method
clientContext.Load(uploadFile);
//Executes previous query
clientContext.ExecuteQuery();
//Calls the updateFields method to update the associated fields of the most recently uploaded image
updateFields();
end = DateTime.Now;
TimeSpan span = end.Subtract(start);
//Writes out to the console to indicate the file has finished being uploaded
Console.WriteLine("Uploaded: {0}", fileName + " Done!");
Console.WriteLine("Time Elapsed: {0}", span.Seconds + "seconds");
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void sharepointLogin()
{
try
{
//Loads credentials needed for authentication
clientContext.Credentials = credentials;
//Loads the site
clientContext.Load(site);
//Loads the site list
clientContext.Load(list);
//Executes the previous queries on the server
clientContext.ExecuteQuery();
//Writes out the title of the SharePoint site to the console
Console.WriteLine("Title: {0}", site.Title);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
Right now, I have to do everything remotely using the client-object model. I can't use SharePoint.Administration to change the max upload size. So does anyone know how, using the client-object model I can get past this problem of not being able to upload files greater than 4MB? Thank you in advance for any help!
This is because of the WCF limit for the client object model. You need to run this on the server from a SharePoint management shell with admin rights:
SPWebService contentService = SPWebService.ContentService;
contentService.ClientRequestServiceSettings.MaxReceivedMessageSize = int.MaxValue; // 2GB
contentService.Update();
More info here
Try to use the SaveBinaryDirect method. The SaveBinaryDirect method use Web Based Distributed Authoring and Versioning (WebDAV) for uploading and downloading files. Without building your own custom WCF service, WebDAV
is the most efficient way to upload and download files.
using (FileStream lp_fs = new FileStream(is_FileToImport, FileMode.OpenOrCreate))
{
Microsoft.SharePoint.Client.File.SaveBinaryDirect(lp_context, lp_uri.LocalPath, lp_fs, true);
}
Microsoft.SharePoint.Client.File lp_newFile = lp_web.GetFileByServerRelativeUrl(lp_uri.LocalPath);
lp_context.Load(lp_newFile);
lp_context.ExecuteQuery();
//check out to make sure not to create multiple versions
lp_newFile.CheckOut();
ListItem lp_item = lp_newFile.ListItemAllFields;
listItem["Created"] = info.SourceFile.CreationTime;
listItem["Modified"] = info.SourceFile.LastWriteTime;
listItem.Update();
// use OverwriteCheckIn type to make sure not to create multiple versions
lp_newFile.CheckIn(string.Empty, CheckinType.OverwriteCheckIn);