I've been trying to get the count of unread emails in Gmail but I'm encountering some problems. I did a search and I found ImapX library that should help me achieve this, but the code I found here on StackOverFlow on previews questions doesn't work. This is my code right now:
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string username = "my_email#gmail.com";
string passwd = "my_pass";
int unread = 0;
ImapClient client = new ImapClient("imap.gmail.com", 993, true);
bool result = client.IsConnected;
if (result)
Console.WriteLine("Connection Established");
result = client.Login(username, passwd); // <-- Error here
if (result)
{
Console.WriteLine("Logged in");
FolderCollection folders = client.Folders;
// Message messages = client.Folders["INBOX"].Messages;
foreach (ImapX.Message m in client.Folders["INBOX"].Messages)
{
if (m.Seen == false)
unread++;
}
Console.WriteLine(unread);
}
}
}
}
The Error is:
The selected authentication mechanism is not supported" on line 26
which is result = client.Login(username, passwd);
Sample from ImapX:
var client = new ImapX.ImapClient("imap.gmail.com", 993, true);
client.Connection();
client.LogIn(userName, userPassword);
var messages = client.Folders["INBOX"].Search("ALL", true);
Maybe you have enabled Two-factor authentication and you need generate application password. Othery way you will receive an e-mail warning that something is trying to access your mailbox and you must add your application as an exception. As an alternate solution you can try https://github.com/jstedfast/MailKit sample code at the bottom of the README
Most likely, gmail is looking for you to do a STARTTLS command, which it appears ImapX does not support. If you look at the response to the IMAPX1 CAPABILITY request, you'll likely see a "LOGINDISABLED" element, which means the server won't accept the "LOGIN" statement yet. So even if you UseSSL, the server (Microsoft Exchange in my case) is still looking for the STARTTLS command before it will let me LOGIN.
You have to make the connection.
ImapClient client = new ImapClient("imap.gmail.com", 993, true);
client.Connect();
bool result = client.IsConnected;
So if you added the line client.Connect(), I think it solves your problem.
Related
Downloading an email from a server works like a charm. I setup the code to download only
emails that don't have a flag seen. And emails are downloaded without any issues.
After I download the email I need to flag it. I tried this:
using (var client = new ImapClient ()) {
client.Connect ("xxx", 993, SecureSocketOptions.SslOnConnect);
client.Authenticate ("yyy", "zzz");
client.Inbox.Open (FolderAccess.ReadOnly);
var uids = client.Inbox.Search (SearchQuery.NotSeen);
//MessageBox.Show(uids[0].ToString());
foreach (var uid in uids) {
var message = client.Inbox.GetMessage (uid);
// write the message to a file
message.WriteTo (string.Format ("{0}.eml", uid));
client.Inbox.SetFlags(uid, MessageFlags.Seen, true);
}
client.Disconnect (true);
"client.Inbox.SetFlags(uid, MessageFlags.Seen, true);"
did I use this correctly? Because it is not marking the downloaded messages. And if i check emails in inbox, they are still as not seen.
Am not sure whether the old method of sending Mail using Mailkit is quite working with this code below
try
{
var emailMessage = new MimeMessage();
emailMessage.From.Add(new MailboxAddress(_emailConfig.SenderName, _emailConfig.SenderAddress));
emailMessage.To.Add(new MailboxAddress(email));
emailMessage.Subject = subject;
var builder = new BodyBuilder
{
HtmlBody = message
};
emailMessage.Body = builder.ToMessageBody();
using var smtp = new SmtpClient
{
ServerCertificateValidationCallback = (s, c, h, e) => true
};
smtp.AuthenticationMechanisms.Remove("XOAUTH2");
await smtp.ConnectAsync(_emailConfig.SmtpServer, Convert.ToInt32(_emailConfig.Port), false).ConfigureAwait(false);
await smtp.AuthenticateAsync(_emailConfig.Username, _emailConfig.Password).ConfigureAwait(false);
await smtp.SendAsync(emailMessage).ConfigureAwait(false);
await smtp.DisconnectAsync(true).ConfigureAwait(false);
}
catch (Exception ex)
{
throw new InvalidOperationException(ex.Message);
}
but am having exceptions if i use the code above to send email
nvalidOperationException: 534: 5.7.14 <https://accounts.google.com/signin/continue?sarp=1&scc=1&plt=AKgnsbv
5.7.14 26mzQKtlwfyEdGzHHdpi3ewWG6skAWgOBbdNNYmwzr9Sg3fGu-KixLAfODpJsVafutidE
5.7.14 8xBOp_8rNCvk9Y6iEcOkDlcZ1d-483zQ1Krw04NvqxQdq3w4iTtC8E9bL8uGprgV>
5.7.14 Please log in via your web browser and then try again.
5.7.14 Learn more at
5.7.14 https://support.google.com/mail/answer/78754 o5sm2555896wmh.8 - gsmtp
so when i changed this line of code below to use SSLS, A new error came out
await smtp.ConnectAsync(_emailConfig.SmtpServer, Convert.ToInt32(_emailConfig.Port), true).ConfigureAwait(false);
Exception returned
InvalidOperationException: An error occurred while attempting to establish an SSL or TLS connection.
This usually means that the SSL certificate presented by the server is not trusted by the system for one or more of
the following reasons:
1. The server is using a self-signed certificate which cannot be verified.
2. The local system is missing a Root or Intermediate certificate needed to verify the server's certificate.
3. A Certificate Authority CRL server for one or more of the certificates in the chain is temporarily unavailable.
4. The certificate presented by the server is expired or invalid.
Another possibility is that you are trying to connect to a port which does not support SSL/TLS.
It is also possible that the set of SSL/TLS protocols supported by the client and server do not match.
See https://github.com/jstedfast/MailKit/blob/master/FAQ.md#SslHandshakeException for possible solutions.
have searched everywhere on how to do it,even turned on my less secured app. some recommended sendGrid, i created a free account with them also,but i dont have access to the account created. Does anyone knows how to fix the code above using Mailkit
Try it like this.
using (var smtpClient = new SmtpClient())
{
smtpClient.ServerCertificateValidationCallback = (s, c, h, e) => true;
await smtpClient.ConnectAsync("host", port, false);
if (smtpClient.Capabilities.HasFlag(SmtpCapabilities.Authentication))
{
smtpClient.AuthenticationMechanisms.Remove("XOAUTH2");
await smtpClient.AuthenticateAsync(username, password);
}
await smtpClient.SendAsync(mailMsg);
smtpClient.Disconnect(true);
}
I am finding that I am unable to connect to outlook.office365.com's IMAP server using AE.Net.Mail. The code is very simple:
this._imapClient = new ImapClient(imapServer, username, password, AE.Net.Mail.AuthMethods.Login, port, enableSSL)
I find that I can connect to GMail with no issues, but office365 outlook will not connect, I keep getting timeouts. I've verified the IMAP settings by putting them in to Outlook and in to Thunderbird.
Has anyone else had trouble connecting AE.Net.Mail to Office365's IMAP server?
AE.Net.Mail is quite buggy and has not seen any development in over a year last I checked. I would recommend using MailKit instead.
I just confirmed that MailKit works with Office365.com with the following code snippet:
using (var client = new ImapClient ()) {
client.Connect ("outlook.office365.com", 993, true);
client.Authenticate ("username", "password");
// get the unread messages
var uids = client.Inbox.Search (SearchQuery.NotSeen);
foreach (var uid in uids) {
var message = client.Inbox.GetMessage (uid);
}
client.Disconnect (true);
}
Hope that helps.
Found I am able to connect by increasing the timeouts:
_imapClient.ServerTimeout = 120000;
_imapClient.IdleTimeout = 120000;
_imapClient.Connect(_imapServer, _port, _enableSSL, false);
_imapClient.Login(_username, _password);
_imapClient.SelectMailbox("Inbox");
I have been trying to use the solution provided in the following link:
using c# .net librarires to check for IMAP messages from gmail servers
But I keep hitting the error:
A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond
Is there something that I am missing here. My Code:
public List<MailMessage> ReadMail()
{
List<MailMessage> msgs;
using (var ic = new AE.Net.Mail.ImapClient("imap.gmail.com", "username#gmail.com", "pass-to-gmail", ImapClient.AuthMethods.Login, 993, true))
{
ic.SelectMailbox("INBOX");
msgs = new List<MailMessage>(ic.GetMessageCount());
msgs = ic.GetMessages(0, 100, false, true).ToList();
ic.Disconnect();
}
return msgs;
}
Use this piece of code to connect to gmail.
ImapX.ImapClient client = null;
client = new ImapX.ImapClient("imap.gmail.com", 993, true);
if (!client.Connection())
{
MessageBox.Show("Couldn't connect to gmail. Check your internet connection and try again");
client = null;
return false;
}
if (!client.LogIn(email, password))
{
MessageBox.Show("Email And/Or password incorrect");
client = null;
return false;
}
I have the following code running in a windows service:
WebClient webClient = new WebClient();
webClient.Credentials = new NetworkCredential("me", "12345", "evilcorp.com");
webClient.DownloadFile(downloadUrl, filePath);
Each time, I get the following exception
{"The remote server returned an error: (401) Unauthorized."}
With the following inner exception:
{"The function requested is not supported"}
I know for sure the credentials are valid, in fact, if I go to downloadUrl in my web browser and put in my credentials as evilcorp.com\me with password 12345, it downloads fine.
What is weird though is that if I specify my credentials as me#evilcorp.com with 12345, it appears to fail.
Is there a way to format credentials?
webClient.UseDefaultCredentials = true; resolved my issue.
Apparently the OS you are running on matters, as the default encryption has changed between OSes.
This blog has more details: http://ferozedaud.blogspot.com/2009/10/ntlm-auth-fails-with.html
This has apparently also been discussed on stackoverflow here: 407 Authentication required - no challenge sent
I would suggest read the blog first as the distilled knowledge is there.
According to the msdn docs the exception could be because the method has been called simultaneously on multiple threads. The DownloadFile method also requires a completely qualified URL such as http://evilcorp.com/.
For me, 'webClient.UseDefaultCredentials = true;' solves it only on local, not in the web app on the server connecting to another server. I couldn't add the needed credential into Windows as a user, but I found later some programming way - I won't test it as I already made own solution. And also I don't want to mangle with the web server's registry, even if I have the needed admin rights. All these problems are because of the Windows internal handling of the NTLM authentication ("Windows Domain") and all of libraries and frameworks built over that (e.g. .NET).
So the solution for me was quite simple in idea - create a proxy app in a multiplatform technology with a multiplatform NTLM library where the NTLM communication is created by hand according to the public specs, not by running the built-in code in Windows. I myself chose Node.js and the httpntlm library, because it's about only one single source file with few lines and calling it from .NET as a program returning the downloaded file (also I prefer transferring it through the standard output instead of creating a temporary file).
Node.js program as a proxy to download a file behind the NTLM authentication:
var httpntlm = require('httpntlm'); // https://github.com/SamDecrock/node-http-ntlm
//var fs = require('fs');
var login = 'User';
var password = 'Password';
var domain = 'Domain';
var file = process.argv.slice(2); // file to download as a parameter
httpntlm.get({
url: 'https://server/folder/proxypage.aspx?filename=' + file,
username: login,
password: password,
workstation: '',
domain: domain,
binary: true // don't forget for binary files
}, function (err, res/*ponse*/) {
if (err) {
console.log(err);
} else {
if (res.headers.location) { // in my case, the server redirects to a similar URL,
httpntlm.get({ // now containing the session ID
url: 'https://server' + res.headers.location,
username: login,
password: password,
workstation: '',
domain: domain,
binary: true // don't forget for binary files
}, function (err, res) {
if (err) {
console.log(err);
} else {
//console.log(res.headers);
/*fs.writeFile("434980.png", res.body, function (err) { // test write
if (err) // to binary file
return console.log("Error writing file");
console.log("434980.png saved");
});*/
console.log(res.body.toString('base64')); // didn't find a way to output
} // binary file, toString('binary')
}); // is not enough (docs say it's
// just 'latin1')...
} else { // if there's no redirect
//console.log(res.headers); // ...so I output base64 and
console.log(res.body.toString('base64')); // convert it back in the caller
} // code
}
});
.NET caller code (the web app downloading files from a web app on another server)
public static string ReadAllText(string path)
{
if (path.StartsWith("http"))
return System.Text.Encoding.Default.GetString(ReadAllBytes(path));
else
return System.IO.File.ReadAllText(path);
}
public static byte[] ReadAllBytes(string path)
{
if (path.StartsWith("http"))
{
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = "node.exe"; // Node.js installs into the PATH
psi.Arguments = "MyProxyDownladProgram.js " +
path.Replace("the base URL before the file name", "");
psi.WorkingDirectory = "C:\\Folder\\With My\\Proxy Download Program";
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
Process p = Process.Start(psi);
byte[] output;
try
{
byte[] buffer = new byte[65536];
using (var ms = new MemoryStream())
{
while (true)
{
int read = p.StandardOutput.BaseStream.Read(buffer, 0, buffer.Length);
if (read <= 0)
break;
ms.Write(buffer, 0, read);
}
output = ms.ToArray();
}
p.StandardOutput.Close();
p.WaitForExit(60 * 60 * 1000); // wait up to 60 minutes
if (p.ExitCode != 0)
throw new Exception("Exit code: " + p.ExitCode);
}
finally
{
p.Close();
p.Dispose();
}
// convert the outputted base64-encoded string to binary data
return System.Convert.FromBase64String(System.Text.Encoding.Default.GetString(output));
}
else
{
return System.IO.File.ReadAllBytes(path);
}
}
Hmm. Lots of answers, but I wonder if answering your last question would have solved everything. "me" is not an authorization type (unless your server has added support for it, of course!). You probably want "Basic".
Also keep in mind that some webservices require you to send the authorization header on the initial request, and this won't do that. Rather it responds with it after getting an authorization required response from the server. If you need this, you need to create your own Authorization header.
String basicToken = Base64Encoding.EncodeStringToBase64(String.Format("{0}:{1}", clientId.Trim(), clientSecret.Trim()));
webClient.Headers.Add("Authorization", String.Format("Basic {0}", basicToken));
And of course as people have pointed out, setting UseDefaultCredentials to true works if you are using IIS (or other windows security aware http server) in a windows environment.