I was able to succeed via a package I found called EAGetMail. Unfortunately, I realized soon after that they have a token system and this is not a free approach.
There are a couple other choices available, like using Outlook Mail REST API, and MimeKit, but I'm lost on how to achieve my end result because no "start to finish" code is available on either of these references that demonstrates how to parse an Inbox for an account.
I've started to write this with the help of Mimekit, but am not sure if this is the proper way at all.
I must imagine it looks something like:
using (var client = new SmtpClient ())
{
client.Connect("outlook.office365.com", 587);
client.Authenticate("myemail#office365account.com", "mypassword");
var message = MimeMessage.Load(stream);
}
I don't know how to setup the stream mentioned above, and I don't know if it's possible to do this with Mimekit and Office 365.
I'm open to seeing a solution for this in any other approach that's not through EAGetMail. If anyone has a lightweight solution ranging from actual establishing a connection, to pulling messages from the inbox, would be great to see!
I've got it using EWS (Exchange Web Services). Here's my code:
private static bool RedirectionUrlValidationCallback(string redirectionUrl)
{
// The default for the validation callback is to reject the URL.
bool result = false;
Uri redirectionUri = new Uri(redirectionUrl);
// Validate the contents of the redirection URL. In this simple validation
// callback, the redirection URL is considered valid if it is using HTTPS
// to encrypt the authentication credentials.
if (redirectionUri.Scheme == "https")
{
result = true;
}
return result;
}
static void Main(string[] args)
{
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
service.Credentials = new WebCredentials("email#myemail.com", "myPassword");
service.AutodiscoverUrl("email#myemail.com", RedirectionUrlValidationCallback);
//creates an object that will represent the desired mailbox
Mailbox mb = new Mailbox(#"email#myemail.com");
//creates a folder object that will point to inbox folder
FolderId fid = new FolderId(WellKnownFolderName.Inbox, mb);
//this will bind the mailbox you're looking for using your service instance
Folder inbox = Folder.Bind(service, fid);
//load items from mailbox inbox folder
if (inbox != null)
{
FindItemsResults<Item> items = inbox.FindItems(new ItemView(100));
foreach (var item in items)
{
item.Load();
Console.WriteLine("Subject: " + item.Subject);
}
}
}
Related
I'm trying to connect to outlook online and download some emails with predefined conditions.
in browser when I go to https://outlook.office.com/mail/ and logon with specific email and pass, I can see all mails.
I have c# console application that should connect, search and manipulate with emails.
public void EmailReceiver()
{
try
{
ExchangeService exchangeService = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
exchangeService.Credentials = new NetworkCredential("username", "pass");
exchangeService.Url = new Uri("https://outlook.office.com/mail/");
var inbox = Folder.Bind(exchangeService, WellKnownFolderName.Inbox);
var sf1 = new SearchFilter.ContainsSubstring(EmailMessageSchema.From, "searchMail");
SearchFilter.SearchFilterCollection searchFilterCollection = new SearchFilter.SearchFilterCollection(LogicalOperator.Or);
searchFilterCollection.Add(sf1);
var view = new ItemView(1000);
var findResults = service.FindItems(WellKnownFolderName.Inbox, searchFilterCollection, view);
return;
//foreach (Item item in findResults)
//{
// GetAttachmentsFromEmail(item.Id);
//}
}
catch (Exception ex)
{
throw;
}
}
But cannot connect to outlook
Any suggestions?
Office 365 no longer allows basic authentication, you need to switch to OAuth2. You can still allow basic auth, but you must explicit do so in the Exchange admin console for your tenant. But even in that case, you need to use WebCredentials class, not NetworkCredential. In case of OAuth, you need OAuthCredentials class.
You might also want to set TraceEnabled property to true and provide your class (assign it to the TraceListener property) to log all EWS calls.
exchangeService.TraceEnabled = true;
exchangeService.TraceListener = new MyTraceListener(this);
exchangeService.TraceFlags = TraceFlags.All;
I am using this below code but unable to read my office 365 Inbox emails from C# code. Once I get this working then later I would need to read emails from shared mailbox.
I’d really appreciate if someone could please help me in fixing this issue or guide me what am I missing here ?
If I use Url as Office365 one then getting this error: "The request failed. The remote server returned an error 401. Unauthorized "
If I use Url as casx16 one (found this in company's Q/A portal) then getting this error: " No mailbox with such GUID "
using Microsoft.Exchange.WebServices.Data;
public static void ReadMyMailbox_2()
{
ExchangeService exchangeService = new ExchangeService();
exchangeService.Credentials = new WebCredentials("ajain", "password ", "MS"); /// ajain is my MSID
// exchangeService.AutodiscoverUrl("a_jain#xyz.com", RedirectionUrlValidationCallback);
exchangeService.Url = new Uri("https://casx16.xyz.com/ews/exchange.asmx");
// exchangeService.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
FolderId mailbox = new FolderId(WellKnownFolderName.Inbox, "a_jain#xyz.com");
ItemView itemView = new ItemView(10);
FindItemsResults<Item> result = exchangeService.FindItems(mailbox, itemView);
foreach (var msg in result)
{
EmailMessage message = EmailMessage.Bind(exchangeService, msg.Id);
Console.WriteLine(message.Subject);
}
}
It looks like you're setting up the ExchangeService object correctly. A couple things to try:
Try passing the full email (a_jain#xyz.com) to WebCredentials
Make sure your the domain you're passing to WebCredentials is correct
Try the WebCredentials constructor that only takes username and password.
Note: I don't think autodiscover will work with O365
recently I have noticed a lot of users being registered to my site and they all use disposable emails, temporary email addresses or fake emails to register on the site...
What I have done to partially prevent that is I added mailgun email verification which tells me whether an email is valid or not , disposable one or not...
This filtered out like 95% of the fake emails and users, however there is still a small portion of them which go "unnoticed". What I have figured out to do are following things via code:
public static bool IsValidEmail(string email)
{
RestClient client = new RestClient();
client.BaseUrl = new Uri("https://api.mailgun.net/v3");
client.Authenticator = new HttpBasicAuthenticator("api", "");
RestRequest request = new RestRequest();
request.Resource = "/address/validate";
request.AddParameter("address", email);
var res = new JavaScriptSerializer().Deserialize<RootObject>(client.Execute(request).Content);
if (res.is_disposable_address || res.is_valid==false)
return false;
return true;
}
This is one step via mailgun that I described previously... And second method to prevent spam/fake users is that I have set up manually a table in my DB with blacklisted domains to which registration won't be allowed... And the logic for that is:
public static bool IsValid(string email)
{
using (var ctx = new myEntities())
{
var blacklistedProviders = ctx.BlacklistedDomains.ToList();
foreach (var item in blacklistedProviders)
{
if (email.ToLower().Contains(item.DomainName.ToLower()))
return false;
}
}
return true;
}
However I don't think this will prevent them from doing more malicious stuff on the website...
I have also added google recaptcha to harden the process of registration for spammers, that does the job well too....
So my questions here are:
Are there any other ways that I can validate the email to see whether it is disposable or invalid email
What other way could I implement to prevent users from creating fake accounts?
Can someone help me out with this?
My company is under Office 365.
My goal is to retrieve Outlook's suggested contacts of the user in my asp .net MVC app (the contacts displayed in the autocomplete list).
The website is configured for automatic logon with Windows Authentication and I don't want to ask the user his credentials.
I have to tried to retrieve the suggested contacts by using Exchange Web Service but I only succeed to retrieve "real" contacts by using this code :
public List<Contact> GetContacts()
{
ContactsFolder.Bind(this._service, WellKnownFolderName.Contacts);
ItemView itemView = new ItemView(1000);
itemView.PropertySet = new PropertySet(BasePropertySet.IdOnly, new PropertyDefinitionBase[4]
{
(PropertyDefinitionBase) ContactSchema.DisplayName,
(PropertyDefinitionBase) ContactSchema.Surname,
(PropertyDefinitionBase) ContactSchema.GivenName,
(PropertyDefinitionBase) ContactSchema.EmailAddress1
});
FindItemsResults<Item> items = this._service.FindItems(WellKnownFolderName.Contacts, (ViewBase) itemView);
List<Contact> list = new List<Contact>();
foreach (Item obj in items)
{
if (obj is Contact)
list.Add(obj as Contact);
}
return list;
}
Then, I tried by using the People Api of Office 365 REST API but I don't know how to make the call without asking for the user login/password. This is a sample of a try (if I don't use the proxy, I receive an HTTP 407 Error) :
public async Task Try()
{
var proxy = WebRequest.GetSystemWebProxy();
proxy.Credentials = new NetworkCredential("foo", "1234");
// Now create a client handler which uses that proxy
HttpClient client = null;
HttpClientHandler httpClientHandler = new HttpClientHandler()
{
Proxy = proxy,
PreAuthenticate = true,
UseDefaultCredentials = false,
Credentials = new NetworkCredential("foo#foo.com", "1234")
};
var httpClient = new HttpClient(httpClientHandler);
var result = await httpClient.GetAsync("https://outlook.office.com/api/beta/me/people");
var stringContent = await result.Content.ReadAsStringAsync();
}
About the Suggested Contact problem
What I am thinking is that you are not looking in the proper folder. From what I have seen by googling the suggested contacts are not in the Contacts directory but in Suggested Contacts. In your EWS sample you are looking in Contacts...
See this discussion. Look also at this guy post, he manages to have access to the Suggested Contacts folder with EWS and powershell so there is no doubt this is feasible with C# and EWS .NET SDK. My advice is to continue trying with your sample 1.
About the Authentication problem
Let me emphasize the fact that your requests should be authorized to access both Exchange Web Services (code sample 1) or the outlook REST API (code sample 2).
In sample 1 we do not see how the _service field is instantiated but I bet there is a piece of code that looks more or less the lines below so your allowed to request EWS.
ExchangeService service = new ExchangeService();
service.Credentials = new OAuthCredentials(token);
service.Url = new Uri(ewsUrl);
The token can be probably reuse for the Outlook REST API, try to set it in the bearer in the httpClient
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "Your Oauth token");
Now your request should be authorized but you still have your proxy problem. I bet that this happen only within your organisation because your IT set up a proxy. You probably won't need it in production. You can use a debug statement to make it work while your developing locally.
#if DEBUG
IWebProxy webProxy = System.Net.WebRequest.DefaultWebProxy;
if (webProxy!=null && !webProxy.IsBypassed(new Uri(endpoint)))
{
client.Proxy = webProxy;
}
#endif
I never found the "Suggested Contacts" folder of the post.
I ended by using the folder "AllContacts" that seems to do the job.
public List<Contact> GetSuggestedContacts()
{
// Instantiate the item view with the number of items to retrieve from the Contacts folder.
ItemView view = new ItemView(1000);
// To keep the request smaller, request only the display name property.
view.PropertySet = new PropertySet(BasePropertySet.FirstClassProperties, ContactSchema.DisplayName, ContactSchema.Surname, ContactSchema.GivenName, ContactSchema.EmailAddress1);
// Retrieve the RecipientCache folder in the Contacts folder that have the properties that you selected.
var contactFolders = _service.FindFolders(new FolderId(WellKnownFolderName.Root), new FolderView(500));
var folder = contactFolders.Folders.SingleOrDefault(x => x.DisplayName == "AllContacts");
if(folder == null) return new List<Contact>();
//Cast Item in Contact and filtered only real adresses
var cacheContacts = folder.FindItems(view).Items.OfType<Contact>().Where(x => x.EmailAddresses.Contains(0) && x.EmailAddresses[0].Address != null && x.EmailAddresses[0].Address.Contains('#')).ToList();
return cacheContacts;
}
I also found the service ResolveName of Exchange that I could have use for the autocomplete.
Using the Exchange Web Services API, is it possible to determine whether a mailbox/e-mail address such as someone#mydomain.com exists within an organization?
If so, which is the simplest way to do this and is it possible without the use of impersonation?
Case: A Windows Service regularly sends e-mails to people within the organization. It does not have any explicit knowledge about their e-mail adresses. It only knows their username and assumes that their e-mail address is username#mydomain.com. This is true for all users except for a few that do not have mailboxes. In these cases, it should not attempt to send the e-mail in the first place.
Solution:
As suggested by mathieu: look for user and e-mail address in Active Directory instead. This function gets the job done:
using System.DirectoryServices.AccountManagement;
// ...
public static bool TryGetUserEmailAddress(string userName, out string email)
{
using (PrincipalContext domainContext =
new PrincipalContext(ContextType.Domain, Environment.UserDomainName))
using (UserPrincipal user =
UserPrincipal.FindByIdentity(domainContext, userName))
{
if (user != null && !string.IsNullOrWhiteSpace(user.EmailAddress))
{
email = user.EmailAddress;
return true;
}
}
email = null;
return false; // user not found or no e-mail address specified
}
Determining if an user has a mailbox with EWS only could be more complicated than expected, especially without impersonation.
If you're in an Active Directory domain, you should rely on the DirectoryEntry information to determine the mailbox of an user, and send email accordingly. If you got your user login, it's really easy to get the associated DirectoryEntry.
there is an easy way to do it by checking the user availability like the following code.
I tried this and it is working for me.
I am not sure about other cases when availability result returns error but for sure when the email is not right it does
to define your exchange service refer to this: https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/get-started-with-ews-managed-api-client-applications
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);//You version
service.Credentials = new WebCredentials("user1#contoso.com", "password");
service.AutodiscoverUrl("user1#contoso.com", RedirectionUrlValidationCallback);
string email = "TEST#YOUR.COM";
// Get User Availability after 6 months
AttendeeInfo attendee = new AttendeeInfo(email);
var attnds = new List<AttendeeInfo>();
attnds.Add(attendee);
var freeTime = service.GetUserAvailability(attnds, new
TimeWindow(DateTime.Now.AddMonths(6), DateTime.Now.AddMonths(6).AddDays(1)), AvailabilityData.FreeBusyAndSuggestions);
//if you receive result with error then there is a big possibility that the email is not right
if(freetimes.AttendeesAvailability.OverallResult == ServiceResult.Error)
{
return false;
}
return true;