I'm trying get unread mails from Gmail by IMAP protocol. I followed this tuto : https://briancaos.wordpress.com/2012/04/24/reading-mails-using-imap-and-mailsystem-net/#comment-4999 , which recieved many good comments.
But when I call
MessageCollection messages = mails.SearchParse("UNSEEN");
I got error message "index and length must refer to a location within the string".
I just call a simple function, so I don't know what's wrong. For more detail, here is my code snippet:
public class MailRepository
{
private Imap4Client _client = null;
public MailRepository(string mailServer, int port, bool ssl, string login, string password)
{
if (ssl)
Client.ConnectSsl(mailServer, port);
else
Client.Connect(mailServer, port);
Client.Login(login, password);
}
public IEnumerable<Message> GetAllMails(string mailBox)
{
return GetMails(mailBox, "ALL").Cast<Message>();
}
public IEnumerable<Message> GetUnreadMails(string mailBox)
{
return GetMails(mailBox, "UNSEEN").Cast<Message>();
}
protected Imap4Client Client
{
get
{
if (_client == null)
_client = new Imap4Client();
return _client;
}
}
private MessageCollection GetMails(string mailBox, string searchPhrase)
{
Mailbox mails = Client.SelectMailbox(mailBox);
MessageCollection messages = mails.SearchParse(searchPhrase);
return messages;
}
}
This error may occur when your mails contain non-ASCII characters. As I don't see an easy fix here and MailSystem.NET is no longer supported, I recommend using an alternative library.
Mailkit seems to be a good option. Also look here for further reference: https://stackoverflow.com/a/23375968
Related
The original post was removed and I thought I would revise my latest issue. I understand completely that it has something to do with my username and password but am not sure of what else I can do. I have rest passwords multiple times, deleted and reestablished the username/email address multiple times and even dumped the .Net SmtpClient for the MailKit approach which I am now getting this error.
I am wonder if it has anything to do with me going through Bluehost for my domain and office365 subscription. With that said, as I began developing this application, I have noticed through Telnet I am still unable to establish a connection. Does anybody have any advice on how to send an email with SMTP (or anyway) through office365/outlook?
Here is my code:
Controller:
[HttpPost]
public async Task<IActionResult> SendContactEmail(ContactCardModel contact)
{
string emailSubject = $"Inquiry from {contact.name} from {contact.organization}";
await _emailSender.SendEmailAsync(contact.name, contact.email, emailSubject, contact.message);
ViewBag.ConfirmMsg = "Sent Successful";
return View("Contact");
}
Email Service:
public class SendEmailService : ISendEmail
{
private string _host;
private string _from;
private string _pwd;
public SendEmailService(IConfiguration configuration)
{
//TODO: Collect SMTP Configuration Settings
var smtpSection = configuration.GetSection("SMTP");
_host = smtpSection.GetSection("Host").Value;
_from = smtpSection.GetSection("From").Value;
_pwd = smtpSection.GetSection("Pwd").Value;
}
public async Task SendEmailAsync(string fromName, string fromEmail, string subject, string message)
{
//TODO: Build MailMessage Object
MimeMessage mailMessage = new MimeMessage();
mailMessage.From.Add(new MailboxAddress(fromName, fromEmail));
mailMessage.To.Add(new MailboxAddress("App Admin", "tyler.crane#odin-development.com"));
mailMessage.Subject = subject;
BodyBuilder bodyBuilder = new BodyBuilder
{
HtmlBody = message
};
//TODO: Build SmtpClient Object and NetworkCredential Object
SmtpClient smtp = new SmtpClient();
smtp.ServerCertificateValidationCallback = (sender, certificate, certChainType, errors) => true;
smtp.AuthenticationMechanisms.Remove("XOAUTH2");
await smtp.ConnectAsync(_host, 587, SecureSocketOptions.StartTls).ConfigureAwait(false);
await smtp.AuthenticateAsync(new NetworkCredential(_from, _pwd)).ConfigureAwait(false);
await smtp.SendAsync(mailMessage).ConfigureAwait(false);
}
}
Interface:
public interface ISendEmail
{
Task SendEmailAsync(
string fromName,
string fromEmail,
string subject,
string message
);
}
Greatly appreciate anybody willing to help!
I finally figured out my own issue and it wasn't even in the slightest bit that difficult. More importantly, the message itself was very misleading and I am here to shed some light for those who are encountering the same issue.
SmtpClient smtp = new SmtpClient();
smtp.ServerCertificateValidationCallback = (s, c, h, e) => true;
// The above Certificate Validation Callback has to be exactly as I show it.
// I, for some reason, had invalid options applied and can assure anyone who
// has followed any tutorial whatsoever, what they have inputted is wrong or for dummy
// testing purposes. Once you have this established, host has to be exactly as
// follows: smpt.office365.com and port 587 ONLY(25 is not longer supported).
smtp.AuthenticationMechanisms.Remove("XOAUTH2");
await smtp.ConnectAsync(_host, 587, SecureSocketOptions.StartTls).ConfigureAwait(false);
await smtp.AuthenticateAsync(new NetworkCredential(_from, _pwd)).ConfigureAwait(false);
await smtp.SendAsync(mailMessage).ConfigureAwait(false);
In no way shape or form did my error apply to the actual account itself. Although this may not directly apply to my issue where the tenant username/pass were not the issue, it may still be an issue for anyone. However, I do highly suggest you consider exactly what my code reflects above with the host and port suggestions I have made.
Thank you all who attempted to try and solve this and if anyone has any additional questions, I would be more than happy to answer them. Thanks!
I am using #jstedfast Mimekit/Mailkit library to send emails from my API. I want to know how to get the delivery status of each email. I tried overriding DeliveryStatusNotification to send notifications on Success:
public class DSNSmtpClient : SmtpClient
{
public DSNSmtpClient()
{
}
protected override string GetEnvelopeId(MimeMessage message)
{
return message.MessageId;
}
protected override DeliveryStatusNotification? GetDeliveryStatusNotifications(MimeMessage message, MailboxAddress mailbox)
{
return DeliveryStatusNotification.Success;
}
}
I am able to send emails like this:
using (DSNSmtpClient dsnSmtpClient = new DSNSmtpClient())
{
dsnSmtpClient.Connect(_emailCredentials.Value.SmtpServer, _emailCredentials.Value.Port, true);
dsnSmtpClient.Authenticate(_emailCredentials.Value.UserName, _emailCredentials.Value.Password);
dsnSmtpClient.Send(mimeMessage);
dsnSmtpClient.Disconnect(true);
}
But I do not get any emails regarding delivery status(or anything) in my sender inbox.
I found some related links:
get the delivery status of email with mimekit/mailkit library
https://github.com/jstedfast/MailKit/issues/602
But they only got me this far.
What else do I need to do to see if an email was delivered or not?
Make sure that your SMTP server supports the DSN extension.
var supportsDsn = client.Capabilities.HasFlag (SmtpCapabilities.Dsn);
BACKGROUND
I have written a little console application that monitors a RabbitMQ queue for emails. Whenever an email is pushed onto the queue, my application would pick up that email, process it and send it.
CODE
Below is the code for my email service, it is what actually sends out the email.
public class MailService : IMailService
{
private readonly SmtpClient _smtpClient = new SmtpClient();
public void SendEmail(string toEmail, string subject, string body, bool isHtml)
{
var emailMessage = BuildEmailMessage(toEmail.Trim(), subject.Trim(), body, isHtml);
_smtpClient.SendAsync(emailMessage, null);
}
#region Helpers
private static MailMessage BuildEmailMessage(string toEmail, string subject, string body, bool isHtml)
{
const string fromEmailAddress = "james.brown#world.com";
var emailMessage = new MailMessage(fromEmailAddress, toEmail, subject, body) { IsBodyHtml = isHtml };
return emailMessage;
}
#endregion
}
PROBLEM
I had 2 emails on the RabbitMQ queue, and when I fired up my console application consumer, it threw the following exception after sending the first email (I received that 1st email in my inbox).
An asynchronous call is already in progress. It must be completed or canceled before you can call this method.
I did some digging around, and saw this thread which explains why I got this. Apparently using SendAsync() isn't the way to go because:
After calling SendAsync, you must wait for the e-mail transmission to complete before attempting to send another e-mail message using Send or SendAsync.
So what is the recommend way of going about this? I can create a new instance of the SmtpClient class for each email, but is that really a good idea?
Send the email synchronously, there's no need to use the older async syntax of SendAsync. Also, ensure your SmtpClient only gets hit from one thread at a time by wrapping it in a using statement. There is a slight performance penalty, but you probably won't notice it unless you're sending a ton of emails. And if you are sending a ton, then overload your MailService.SendEmail method to accept an IEnumerable<EmailModel> and send them all at once using a single SmtpClient.
public void SendEmail(string toEmail, string subject, string body, bool isHtml)
{
var emailMessage = BuildEmailMessage(toEmail.Trim(), subject.Trim(), body, isHtml);
using(var client = new SmtpClient())
{
_smtpClient.Send(emailMessage);
}
}
//this would be the right way to do async
public async Task SendEmailAsync(string toEmail, string subject, string body, bool isHtml)
{
var emailMessage = BuildEmailMessage(toEmail.Trim(), subject.Trim(), body, isHtml);
using(var client = new SmtpClient())
{
_smtpClient.SendMailAsync(emailMessage);
}
}
//this would be the right way to do multiple emails
//you'd need to create an EmailModel class to contain all the details for each email (similar to MailMessage, but it would prevent your own code from taking a dependency on System.Net.Mail
public void SendEmail(IEnumerable<EmailModel> emailModels)
{
var mailMessages = emailModels.Select(em => ConvertEmailModelToMailMessage(em));
using(var client = new SmtpClient())
{
foreach(var mailMessage in mailMessages)
{
//you may want some error handling on the below line depending on whether you want all emails to attempt to send even if one encounters an error
_smtpClient.Send(mailMessage);
}
}
}
private MailMessage ConvertEmailModelToMailMessage(EmailModel emailModel)
{
//do conversion here
}
I use this class to send mails trough a gmail account:
public class GmailAccount
{
public string Username;
public string Password;
public string DisplayName;
public string Address
{
get
{
return Username + "#gmail.com";
}
}
private SmtpClient client;
public GmailAccount(string username, string password, string displayName = null)
{
Username = username;
Password = password;
DisplayName = displayName;
client = new SmtpClient
{
Host = "smtp.gmail.com",
Port = 587,
EnableSsl = true,
DeliveryMethod = SmtpDeliveryMethod.Network,
UseDefaultCredentials = false,
Credentials = new NetworkCredential(Address, password)
};
}
public void SendMessage(string targetAddress, string subject, string body, params string[] files)
{
MailMessage message = new MailMessage(new MailAddress(Address, DisplayName), new MailAddress(targetAddress))
{
Subject = subject,
Body = body
};
foreach (string file in files)
{
Attachment attachment = new Attachment(file);
message.Attachments.Add(attachment);
}
client.Send(message);
}
}
Here is an example of how I use it:
GmailAccount acc = new GmailAccount("zippoxer", "******", "Moshe");
acc.SendMessage("zippoxer#gmail.com", "Hello Self!", "like in the title...", "C:\\822d14ah857.r");
The last parameter in the SendMessage method is the location of an attachment I want to add.
I tried sending a mail with an attachment of 400KB, worked great (even 900KB works). But then I tried uploading an attachment of 4MB, didn't work. Tried 22MB -> didn't work too.
There should be a limit of 25MB per message in Gmail. My message's subject and body are almost empty so don't consider them as part of the message's size. Why do I have that low limit?
According to this post, it is a bug in .Net 4.0. The limit specified in the post is 3,050,417 bytes. You can try the work-around code included in the post. Hope this helps.
http://connect.microsoft.com/VisualStudio/feedback/details/544562/cannot-send-e-mails-with-large-attachments-system-net-mail-smtpclient-system-net-mail-mailmessage
It's still possible to send. Just change the attachment encoding to something other than Base64. I tried testing this and found that there is a IndexOutOfBoundsException in the Base64 encoding code. I was able to successfully send an 11MB file to myself using TransferEncoding.SevenBit.
Check and see if the SmtpClient object is going out of scope or otherwise being disposed before the send is complete and has sent the QUIT to the server.
Okay, this is a bug in .net 4.
Microsoft says it will be fixed in the next service pack.
I am writing a small utility to help process some MySQL tasks every night and have it email my personal email if it fails (this is a personal project, so no company smtp server or anything, emails going through public outlook accounts).
I tested about 5 times and each send was successful, but now any attempts to send email I get this exception:
Error sending test email: Transaction failed. The server response was: 5.2.0 STOREDRV.Submission.Exception:OutboundSpamException; Failed to process message due to a permanent exception with message WASCL UserAction verdict is not None. Actual verdict is Suspend, ShowTierUpgrade. OutboundSpamException: WASCL UserAction verdict is not None. Actual verdict is Suspend, ShowTierUpgrade.[Hostname=BY2PR0101MB1461.prod.exchangelabs.com]
A bit of an oops on my part - didn't think Outlook would consider it as spam on the 6th try - is there anything I can do in Outlook to correct this?
I am using a service account I created in outlook to send these emails to my personal inbox.
The actual code in question:
class JobMailer
{
private string email_to;
private string email_from;
private string password;
private string email_smtp;
private bool use_ssl;
private int port;
public void Send(string subject, string body)
{
MailMessage mail = new MailMessage(email_from, email_to);
using (SmtpClient client = new SmtpClient
{
DeliveryMethod = SmtpDeliveryMethod.Network,
UseDefaultCredentials = false,
EnableSsl = use_ssl,
Host = email_smtp,
Timeout = 100000,
Port = port,
Credentials = new NetworkCredential(email_from, password)
})
{
mail.Subject = subject;
mail.Body = body;
client.Send(mail);
}
}
public JobMailer(string emailTo, string smtp, string emailFrom, string pw, int p, bool ssl)
{
email_to = emailTo;
email_from = emailFrom;
password = pw;
email_smtp = smtp;
port = p;
use_ssl = ssl;
}
}
I resolved this by verifying the account I was trying to use. Each time you encounter this error an email is sent to the account with instructions on what you need to do to resolve the error. Typically you will need to verify against a phone number.
Got this error trying to send lots of emails to myself at Outlook.com, using SMTP.
To fix it I simply added a 5 second delay between the sends, for example:
foreach(var mail in mailToSend)
{
await smtpClient.SendMailAsync(mail);
Console.WriteLine("Sent email: " + mail);
await Task.Delay(5000);
}
If you aren't doing this just as a test, then you can contact the Outlook.com team and ask them to whitelist your IP (make sure you have SPF, rDNS, etc. setup first).