How to get the MessageId from all exchange items - c#

Hello I recently got into development around EWS. One of the issue came up to me is that a client ask me to import emails into database and he wants to detect the duplicate based on InternetMessageID this way he doesn't have to import the duplicate emails and my code came up to this point.
private static string GetInternetMessageID(Microsoft.Exchange.WebServices.Data.Item email)
{
EmailMessage emailMsg = email as EmailMessage;
string returnId = string.Empty;
if ((emailMsg != null)) {
try {
emailMsg.Load();
//loads additional info, without calling this ToRecipients (and more) is empty
} catch (ArgumentException ex) {
//retry
email.Load();
}
returnId = emailMsg.InternetMessageId;
} else {
//what to do?
}
return returnId;
}
I can handle regular emails, but for special exchange objects such as contact, Calendar, Posts etc it does not work because it could not cast it to an EmailMessage object.
And I know you can extract the internetMessageId from those objects. Because the client used to have another software that extract this ID for them, maybe the property is not called internetMessageID, I think I probally have to extract it from the internetMessageHeader. However when ever I try to get it from the item object it just throws me an error. How do I get the internet messageID from these "Special" exchange items?
PS i am aware of item.id.UniqueID however that is not what I want as this id changes if I move items from folder to another folder in exchange

Only objects that have been sent via the Transport service will have an InternetMessageId so things like Contacts and Tasks because they aren't messages and have never been routed via the Transport service will never have an Internet MessageId. You probably want to look at using a few properties to do this InternetMessageId can be useful for messages PidTagSearchKey https://msdn.microsoft.com/en-us/library/office/cc815908.aspx is one that can be used (if you good this there are various examples of using this property).
If your going to use it in Code don't use the method your using to load the property on each item this is very inefficient as it will make a separate call for each object. Because these I'd's are under 256 Kb just retrieve then when using FindItems. eg
ExtendedPropertyDefinition PidTagSearchKey = new ExtendedPropertyDefinition(0x300B, MapiPropertyType.Binary);
ExtendedPropertyDefinition PidTagInternetMessageId = new ExtendedPropertyDefinition(0x1035, MapiPropertyType.String);
PropertySet psPropSet = new PropertySet(BasePropertySet.IdOnly);
psPropSet.Add(PidTagSearchKey);
psPropSet.Add(PidTagInternetMessageId);
ItemView ItemVeiwSet = new ItemView(1000);
ItemVeiwSet.PropertySet = psPropSet;
FindItemsResults<Item> fiRess = null;
do
{
fiRess = service.FindItems(WellKnownFolderName.Inbox, ItemVeiwSet);
foreach (Item itItem in fiRess)
{
Object SearchKeyVal = null;
if (itItem.TryGetProperty(PidTagSearchKey, out SearchKeyVal))
{
Console.WriteLine(BitConverter.ToString((Byte[])SearchKeyVal));
}
Object InternetMessageIdVal = null;
if (itItem.TryGetProperty(PidTagInternetMessageId, out InternetMessageIdVal))
{
Console.WriteLine(InternetMessageIdVal);
}
}
ItemVeiwSet.Offset += fiRess.Items.Count;
} while (fiRess.MoreAvailable);
If you need larger properties like the Body using the LoadPropertiesForItems Method https://blogs.msdn.microsoft.com/exchangedev/2010/03/16/loading-properties-for-multiple-items-with-one-call-to-exchange-web-services/

Related

Updating MetaData on Connected account fails

I am using stripe connect(destination payment) with the help of stripe.net library from Jaymedavis.
The problem that I am facing is that I am not able to retrieve the destination payment ID to update the metadata in the connected account. The following line returns a null preventing me from updating meta data on the connected account. But the strange thing is that when I log in to the dashboard the destination payment ID exists. I am not sure why I am not able to retreive it in code.
Is the charge creation asynchronous?. I am not sure. Stripe's connect documentation does not help either. The following line returns a null. My code is down below. Seeking help.
String deschargeID = result.Transfer.DestinationPayment;
Here is the code that I am using
var service = new StripeChargeService(ZambreroSecretKey);
var result = (Stripe.StripeCharge) null;
try {
result = service.Create(newCharge);
if (result.Paid) {
//get the chargeID on the newgen account and update the metadata.
//Returns null even though it exists in the dashboard
String deschargeID = result.Transfer.DestinationPayment;
var chargeService = new StripeChargeService(newgenSecretKey);
StripeCharge charge = chargeService.Get(deschargeID);
charge.Metadata = myDict;
Response.Redirect("PgeCustSuccess.aspx?OrderID=" + OrderID);
}
} catch (StripeException stripeException) {
Debug.WriteLine(stripeException.Message);
stripe.Text = stripeException.Message;
}
The charge object's transfer attribute is not expanded by default, meaning it's just a string with the ID of the transfer object ("tr_..."), not a full transfer object.
According to Stripe.net's documentation, you can expand the transfer attribute by adding this line:
service.ExpandTransfer = True
before sending the charge creation request.

ServiceObjectPropertyException when trying to get emails with Exchange Webservices?

I am trying to get unread emails and then mark them as read.
I am getting this exception when I run the code:
ServiceObjectPropertyException was unhandeld:
An unhandled exception of type
'Microsoft.Exchange.WebServices.Data.ServiceObjectPropertyException'
occurred in Microsoft.Exchange.WebServices.dll
This error occurred when I try to map my Exchange mail object to my business model object.
This is the Map method:
class MailMapper
{
public static PhishingMail Map(EmailMessage OutlookMail)
{
//Map Exchange email object op Business model email object
PhishingMail readMail = new PhishingMail();
readMail.Subject = OutlookMail.Subject;
return readMail;
}
}
This is the code where it should mark emails as read.
public List<PhishingMail> GetEmails()
{
phishingMailList = new List<PhishingMail>();
FolderId InboxId = new FolderId(WellKnownFolderName.Inbox, "A*******m#i*****nl");
FindItemsResults<Item> findResults = service.FindItems(InboxId, new ItemView(100));
foreach (Item phishingmail in findResults.Items)
{
((EmailMessage)phishingmail).Load(new PropertySet(BasePropertySet.IdOnly, EmailMessageSchema.IsRead));
if (!((EmailMessage)phishingmail).IsRead)
{
((EmailMessage)phishingmail).IsRead = true;
((EmailMessage)phishingmail)
.Update(ConflictResolutionMode.AutoResolve);
}
PhishingMail mail = MailMapper.Map((EmailMessage)phishingmail);
phishingMailList.Add(new PhishingMail());
/// Console.WriteLine(mail.Subject);
}
return phishingMailList;
}
What am I doing wrong? What is wrong with map method?
When you load an item, you speficy the properties to load.
item.Load(new PropertySet(PropertySet.FirstClassProperties));
If you need more properties after loading, you can do the following:
Service.LoadPropertiesForItems(items, PropertySet.FirstClassProperties);
I think the issue is that when you Load the email you're only asking for the ID and IsRead properties. Since your map routine fetches the Subject property, you would need to add that to the Load as well.

EWS - Determine if an e-mail is a reply or has been forwarded

I am using the Exchange Web Services Managed API 2.2 to monitor users inboxes and need to determine if an e-mail is a new item, a reply or a forwarded message.
I have seen various articles on SO such as how to notice if a mail is a forwarded mail? and Is there a way to determine if a email is a reply/response using ews c#? which both help in their specific cases but I still cannot work out how to distinguish between a reply and a forwarded item.
In the first article an extra 5 bytes is added each time (forward or reply) so I don't know what the last action was.
The second article suggests using the InReplyTo however when I examine the property for forwarded e-mails it contains the original senders e-mail address (not null).
I have seen other articles such as this or this that suggest using extended properties to examine the values in PR_ICON_INDEX, PR_LAST_VERB_EXECUTED and PR_LAST_VERB_EXECUTION_TIME.
My code looks as follows but never returns a value for lastVerbExecuted
var lastVerbExecutedProperty = new ExtendedPropertyDefinition(4225, MapiPropertyType.Integer);
var response = service.BindToItems(newMails, new PropertySet(BasePropertySet.IdOnly, lastVerbExecutedProperty));
var items = response.Select(itemResponse => itemResponse.Item);
foreach (var item in items)
{
object lastVerb;
if (item.TryGetProperty(lastVerbExecutedProperty, out lastVerb))
{
// do something
}
}
PR_ICON_INDEX, PR_LAST_VERB_EXECUTED and PR_LAST_VERB_EXECUTION_TIME would only work to tell you if the recipient has acted on a message in their Inbox. Eg if the user had replied or forwarded a message in their inbox then these properties get set on the message in their Inbox. On the message that was responded to or forwarded these properties would not be set. I would suggest you use the In-Reply-To Transport header which should be set on any message that is replied to or forwarded, this should contain the internet messageid of the message that was replied to or forwarded eg.
FindItemsResults<Item> fiRs = service.FindItems(WellKnownFolderName.Inbox, new ItemView(10));
PropertySet fiRsPropSet = new PropertySet(BasePropertySet.FirstClassProperties);
ExtendedPropertyDefinition PR_TRANSPORT_MESSAGE_HEADERS = new ExtendedPropertyDefinition(0x007D, MapiPropertyType.String);
fiRsPropSet.Add(PR_TRANSPORT_MESSAGE_HEADERS);
service.LoadPropertiesForItems(fiRs.Items, fiRsPropSet);
foreach (Item itItem in fiRs)
{
Object TransportHeaderValue = null;
if(itItem.TryGetProperty(PR_TRANSPORT_MESSAGE_HEADERS,out TransportHeaderValue)) {
string[] stringSeparators = new string[] { "\r\n" };
String[] taArray = TransportHeaderValue.ToString().Split(stringSeparators, StringSplitOptions.None);
for (Int32 txCount = 0; txCount < taArray.Length; txCount++)
{
if (taArray[txCount].Length > 12)
{
if (taArray[txCount].Substring(0, 12).ToLower() == "in-reply-to:")
{
String OriginalId = taArray[txCount].Substring(13);
Console.WriteLine(OriginalId);
}
}
}
}
}
Apart from the Subject prefix that was discussed in the other link I don't know of any other proprieties that will differentiate between a reply or forward.
Cheers
Glen
The best way to rely is on the ResponeCode of Extended properties
Refer below scripts
private static int IsForwardOrReplyMail(ExchangeService service, EmailMessage messageToCheck)
{
try
{
// Create extended property definitions for PidTagLastVerbExecuted and PidTagLastVerbExecutionTime.
ExtendedPropertyDefinition PidTagLastVerbExecuted = new ExtendedPropertyDefinition(0x1081, MapiPropertyType.Integer);
ExtendedPropertyDefinition PidTagLastVerbExecutionTime = new ExtendedPropertyDefinition(0x1082, MapiPropertyType.SystemTime);
PropertySet propSet = new PropertySet(BasePropertySet.IdOnly, EmailMessageSchema.Subject, PidTagLastVerbExecutionTime, PidTagLastVerbExecuted);
messageToCheck = EmailMessage.Bind(service, messageToCheck.Id, propSet);
// Determine the last verb executed on the message and display output.
object responseType;
messageToCheck.TryGetProperty(PidTagLastVerbExecuted, out responseType);
if (responseType != null && ((Int32)responseType) == 104)
{
//FORWARD
return 104;
}
else if (responseType != null && ((Int32)responseType) == 102)
{
//REPLY
return 102;
}
}
catch (Exception)
{
return 0;
//throw new NotImplementedException();
}
}
To determine if it was a reply to a email, you can use the EmailMessage objects InReplyTo property, e.g:
EmailMessage mail = ((EmailMessage)Item.Bind(service, new ItemId(UniqueId)));
if (mail.InReplyTo == null)
return;
else
..your code

Unable to fetch built in properties of email along with the extended property

I am working with Exchange Web Services Managed API. I am adding a single extended property to mail items in inbox as they get processed based on some condition. Thus, not all mails will get this extended property attached to them.
Next I am refetching all mails in inbox and if they have this property attached to them, I process them again.
Below is the simple method getAllMailsInInbox(), I have written to refetch the mails in inbox:
class MyClass
{
private static Guid isProcessedPropertySetId;
private static ExtendedPropertyDefinition isProcessedPropertyDefinition = null;
static MyClass()
{
isProcessedPropertySetId = new Guid("{20F3C09F-7CAD-44c6-BDBF-8FCB324244}");
isProcessedPropertyDefinition = new ExtendedPropertyDefinition(isProcessedPropertySetId, "IsItemProcessed", MapiPropertyType.String);
}
public List<EmailMessage> getAllMailsInInbox()
{
List<EmailMessage> emails = new List<EmailMessage>();
ItemView itemView = new ItemView(100, 0);
FindItemsResults<Item> itemResults = null;
PropertySet psPropSet = new PropertySet(BasePropertySet.IdOnly);
itemView.PropertySet = psPropSet;
PropertySet itItemPropSet = new PropertySet(BasePropertySet.IdOnly,
ItemSchema.Attachments,
ItemSchema.Subject,
ItemSchema.Importance,
ItemSchema.DateTimeReceived,
ItemSchema.DateTimeSent,
ItemSchema.ItemClass,
ItemSchema.Size,
ItemSchema.Sensitivity,
EmailMessageSchema.From,
EmailMessageSchema.CcRecipients,
EmailMessageSchema.ToRecipients,
EmailMessageSchema.InternetMessageId,
ItemSchema.MimeContent,
isProcessedPropertyDefinition); //***
itemResults = service.FindItems(WellKnownFolderName.Inbox, itemView);
service.LoadPropertiesForItems(itemResults.Items, itItemPropSet);
String subject = itItem.Subject; //Exception: "You must load or assign this property before you can read its value."
//....
}
}
As you can see, on call service.LoadPropertiesForItems(), it does not load any properties, thus resulting in You must load or assign this property before you can read its value. exception while accessing any of those properties.
If I remove isProcessedPropertyDefinition from the itItemPropSet property set, it fetches all the properties properly.
So can I just know how can I fetch all built in EmailMessage properties along with the extended property?
Your GUID is two digits too short after the last dash. Strange that you're not seeing a FormatException. Still, you should update your code to inspect the GetItemResponse for each item. That way if some error occurs on one item, your code can be aware of it. That means you'll need to make another collection to return.
Update your code with this:
ServiceResponseCollection<ServiceResponse> responses = service.LoadPropertiesForItems(itemResults.Items, itItemPropSet);
foreach (ServiceResponse response in responses)
{
if (response.Result == ServiceResult.Error)
{
// Handle the error associated
}
else
{
String subject = (response as GetItemResponse).Item.Subject;
}
}
Instead of doing
service.LoadPropertiesForItems(itemResults.Items, itItemPropSet);
Try doing
itemResult.LoadPropertiesForItems(itItemPropSet);
Casue once you have the item, you can load the extended property of the item by loading the specific one.

InterIMAP, Viewing UNREAD IMAP mail and Downloading Attachments in C#

I was wondering if anyone can help me on this cause its driving me mad trying get this working
I was working with the trail of mail.dll from http://www.lesnikowski.com/mail/ which is an extremely fantastic tool which unfortunately i cannot afford being a student (even though its around 150eur, its still very expensive to me :/) and this would be a small module in my thesis and my faculty cannot afford to buy these things for students either :/ so anyway I had to go for a free tool (so please dont suggest any non open source ones - trust me i have tried them ALL)..
Well, i'm trying to explore InterIMAP, and for several hours have been trying to list unread emails from my gmail account but it just doesn't seem to be working. I can connect just fine but finding the unread emails seems to be no easy task.. I have tried countless approaches but non seem to give me unread emails in my inbox (I have loads of emails in my inbox and i just want the unread ones). Would someone please assist me? I have been trying to get this working for ages now, but documentation is rather lacking and my every attempt has resulted in a fail so far.
Please help!!
Some code i currently have:
` IMAPConfig config = new IMAPConfig("myhost", "username", "pass", true, true, "");
config.CacheFile = "";
IMAPClient client = null;
try
{
client = new IMAPClient(config, null, 5);
}
catch (IMAPException e)
{
Console.WriteLine(e.Message);
return;
}
Console.WriteLine(DateTime.Now.ToString());
IMAPFolder f = client.Folders["INBOX"];
IMAPSearchResult sResult = f.Search(IMAPSearchQuery.QuickSearchNew()); // <--- Gives me no results even though i do have unread messages!
If you did't reach your goal, here we go:
You should code in the following way:
1st: Inside your SearchQuery class, add a new property "unread", for example.
2nd: Add a new Method that returns an IMAPSearchQuery. It'll quick search unread mails. Something like that:
public static IMAPSearchQuery QuickSearchUnread()
{
IMAPSearchQuery query = new IMAPSearchQuery();
query.unread = true;
return query;
}
3td: Inside your class IMAPFolder, you have a method called that will return an IMAPSearchResult type and that receives an IMAPSearchQuery as parameter.
This method "build" your query with IMAP command queries (IMAP based protocol).
To the Unread query you should add:
public IMAPSearchResult Search(IMAPSearchQuery query)
{
...
if (query.Unread)
searchTerms.Add("UNSEEN");
.
.
...
}
4th: Call the Search method with the new QuickSearch:
config.CacheFile = "";
IMAPClient client = null;
try
{
client = new IMAPClient(config, null, 5);
}
catch (IMAPException e)
{
Console.WriteLine(e.Message);
return;
}
Console.WriteLine(DateTime.Now.ToString());
IMAPFolder f = client.Folders["INBOX"];
IMAPSearchResult sResult = f.Search(IMAPSearchQuery.QuickSearchUnread());
Let me know about your progress.
I hope it can be helpful.
Bye.
I honestly just ended up using Mail.dll trial version as interIMAP was not working properly for me and way to slow because it indexes the emails for some reason :s

Categories