24 02 2016
Como receber e-mails no C# utilizando POP3?
Um tempo atrás eu mostrei aqui no site como enviar e-mails com C#. Porém, como você obviamente sabe, todo e-mail que é enviado, chega em algum lugar (nem que seja na caixa de spam ou de volta para você quando o endereço está errado). No artigo de hoje, veremos o oposto do que eu mostrei anteriormente, ou seja, como receber e-mails no C# utilizando POP3.
Quando falamos de recebimento de e-mails, temos que primeiramente estabelecer de qual protocolo estamos falando. Os dois principais protocolos para recebimento de e-mail são o POP3 e o IMAP.
Esses dois protocolos utilizam conceitos completamente diferentes um do outro, dessa forma, se eu fosse abordar os dois protocolos em um só artigo, ele ficaria imenso. Portanto, decidi começar com o protocolo POP3. Pretendo abordar as mesmas funcionalidades utilizando o IMAP em um artigo futuro.
Particularidades do POP3
O POP3, ao contrário do IMAP, é um protocolo muito simples. Basicamente ele funciona baseado em uma conexão TCP/IP entre o cliente e o servidor, na qual o cliente envia comandos para receber ou deletar e-mails. Ou seja, basicamente essas são as duas possibilidades que temos com o protocolo POP3: pegar um ou mais e-mails do servidor e apagar um ou mais e-mails do servidor.
Em outras palavras, com o protocolo POP3, não dá para saber se o e-mail já foi lido anteriormente e não temos suporte a pastas (só “caixa de entrada“). Isso quer dizer que, se você tiver mais de um dispositivo conectado ao mesmo servidor (o que é absurdamente comum hoje em dia), os e-mails serão baixados duas vezes (uma vez em cada dispositivo). E, mesmo que você tenha marcado um e-mail como “lido” em um dispositivo, ele continuará marcado como “não lido” no outro dispositivo, uma vez que o POP3 não controla esse tipo de estado.
Para mais informações sobre o POP3, consulte a enciclopédia das verdades absolutas, a Wikipedia (hehehe): Post Office Protocol. Ah, e caso você se interesse, tem também alguns artigos que mostram como fazer o recebimento de e-mails com o POP3 fazendo a conexão TCP/IP “na mão“, utilizando diretamente os comandos do protocolo:
Retrieve Mail From a POP3 Server Using C#
Obviamente, esse tipo de implementação você só deve seguir caso queira aprender como funciona as “entranhas” do protocolo POP3. Para implementar o recebimento de e-mails em aplicações de negócios, recomendo que você utilize uma biblioteca para facilitar a sua vida. Isso é o que veremos mais para frente nesse artigo, mas, primeiramente vamos conferir as…
Particularidades do POP3 do Gmail
Além de termos que lidar com as particularidades do POP3 em geral, alguns provedores fizeram o grande favor de implementar o protocolo com algumas particularidades extras. Esse é o caso, por exemplo, do Gmail (já ouviu falar dele?).
Ao utilizarmos o POP3 do Gmail, uma vez que os e-mails tenham sido recebidos por um dispositivo, eles não são mais recebidos em nenhum outro lugar! Ou seja, se você tem dois ou mais dispositivos utilizando o POP3 do Gmail com a mesma conta, uma vez que você recebeu os e-mails em um dispositivo, já era. Os outros dispositivos nunca verão a cara desses e-mails que já foram recebidos no primeiro dispositivo. Que baita sacanagem, não?
Para contornarmos essa limitação, podemos utilizar o prefixo “recent:” antes do usuário da conta. Dessa forma, todos os e-mails dos últimos 30 dias serão retornados, independentemente se eles já foram baixados em algum outro dispositivo ou não. Ou seja, ao invés de utilizarmos “seuemail@gmail.com” como usuário, basta utilizarmos “recent:seuemail@gmail.com“, entendeu?
Outra particularidade do Gmail é uma opção existente nas configurações da conta que, caso esteja ativada, os e-mails serão automaticamente deletados do servidor uma vez que eles tenham sido baixados através do protocolo POP3! Portanto, antes de continuarmos, caso você vá utilizar o POP3 do Gmail, confira como esta opção está configurada na sua conta:
Para mais informações sobre as particularidades do POP3 do Gmail, confira esta thread no StackOverflow:
Bibliotecas para trabalhar com POP3 no C#
Como já mencionei anteriormente de uma forma mais sutil, só um maluco implementaria o POP3 em uma aplicação comercial sem utilizar uma biblioteca. Dito isso, quais seriam, então, as nossas opções de bibliotecas para trabalhar com o POP3 em aplicações .NET?
Primeiramente, temos as opções de bibliotecas comerciais (que eu não testei nenhuma, para ser bem sincero). Nesta thread do StackOverflow você encontra algumas sugestões de bibliotecas comerciais (que implementam tanto o protocolo POP3 quanto o protocolo IMAP):
Suggestions for a .NET Pop3 Library
Para facilitar a sua vida, estas são as bibliotecas comerciais que foram sugeridas na thread acima:
Limilabs Mail.dll (tem também um exemplo da implementação com essa biblioteca no MSDN Code Gallery)
Além das bibliotecas comerciais citadas acima, temos também a biblioteca OpenPop.NET, que é open source e implementa as funcionalidades do POP3. É justamente essa biblioteca que vamos utilizar nos exemplos deste artigo.
Recebendo e-mails com a biblioteca OpenPop.NET
A primeira coisa que temos que fazer para utilizarmos a biblioteca OpenPop.NET é adicionarmos uma referência no nosso projeto. Para isso, você tem duas opções. A primeira delas é baixar os assemblies no SourceForge e adicionar as referências manualmente no projeto. A segunda opção (que eu prefiro por ser muito mais fácil) é simplesmente adicionarmos a referência através do NuGet.
Para adicionar a referência utilizando o NuGet, vá até a tela de gestão de pacotes do NuGet e procure por “OpenPop.NET“. Se você não sabe como adicionar referências do NuGet, não se preocupe. Eu já expliquei isso em um outro artigo.
Uma vez adicionada a referência no projeto, vamos partir para a implementação. Como o foco deste artigo é mostrar como receber os e-mails, vamos criar um formulário bem simples em um projeto do tipo “Windows Forms Application“. Esse formulário terá um botão, um ListBox, três TextBoxes e um DateTimePicker, conforme podemos conferir nesta imagem:
A lógica será a seguinte: ao clicarmos no botão (carregarButton), iremos baixar os e-mails de um servidor qualquer e o ListBox (emailsListBox) mostrará a lista com os assuntos dos e-mails baixados. Ao clicarmos em um item do ListBox, carregaremos as informações de remetente (deTextBox), destinatário (paraTextBox), data de envio (dataDateTimePicker) e o conteúdo em texto do e-mail (conteudoTextBox).
Além desses controles, precisamos adicionar também um BindingSource no nosso formulário. Ele será responsável pelo binding entre o ListBox e os TextBoxes (de forma que, quando o usuário selecionar um item no ListBox, as suas informações sejam automaticamente carregadas nos TextBoxes). Para isso, basta arrastar um BindingSource (que está na caixa de ferramentas, categoria “Data“) para dentro do formulário, dando o nome de “emailsBindingSource“.
Antes de continuarmos, vamos adicionar uma classe bem simples no nosso projeto que servirá para representar cada e-mail que será baixado do servidor. Criaremos essa classe para utilizarmos no data-binding do ListBox e TextBoxes:
public class Email { public string Id { get; set; } public string Assunto { get; set; } public string De { get; set; } public string Para { get; set; } public DateTime Data { get; set; } public string ConteudoTexto { get; set; } public string ConteudoHtml { get; set; } }
Feito isso, vamos ao code-behind do nosso formulário para adicionarmos alguns atributos:
private List<Email> _emails = new List<Email>(); private string _hostname = "pop.gmail.com"; // Host do seu servidor POP3. Por exemplo, pop.gmail.com para o servidor do Gmail. private int _port = 995; // Porta utilizada pelo host. Por exemplo, 995 para o servidor do Gmail. private bool _useSsl = true; // Indicação se o servidor POP3 utiliza SSL para autenticação. Por exemplo, o servidor do Gmail utiliza SSL, então, "true". private string _username = "recent:seuemail@gmail.com"; // Usuário do servidor POP3. Por exemplo, seuemail@gmail.com. private string _password = "SUA_SENHA"; // Senha do servidor POP3.
Nota: obviamente, só estamos adicionando essas informações diretamente no formulário por se tratar de um exemplo. Em uma aplicação de verdade, por favor, armazene essas informações em um arquivo de configuração.
Nota 2: lembre-se que, caso você esteja utilizando o servidor POP3 do Gmail, você deve ficar atento às particularidades listadas anteriormente. Ou seja, para baixar todos os e-mails dos últimos 30 dias mesmo que eles já tenham sido baixados em outro dispositivo, adicione “recent:” antes do nome do usuário. Caso contrário, você só receberá os e-mails que ainda não foram baixados em nenhum dispositivo.
Agora que já temos todas as informações necessárias para conectarmos ao servidor POP3, podemos prosseguir com a implementação do clique no botão “Carregar“. Para isso, primeiramente temos que criar uma instância de Pop3Client dentro de um bloco “using” (para que a conexão seja fechada automaticamente quando sairmos do bloco “using“). Dentro desse bloco, utilizamos os métodos Connect e Authenticate para estabelecermos a conexão e nos autenticarmos com o servidor:
using (var client = new OpenPop.Pop3.Pop3Client()) { client.Connect(_hostname, _port, _useSsl); client.Authenticate(_username, _password); }
Após isso, temos que perguntar para o servidor qual é quantidade de e-mails que será baixada. Precisamos disso porque faremos um “loop” pelas mensagens para baixar uma a uma. O método GetMessageCount é método responsável por retornar a quantidade de mensagens.
Com a quantidade de mensagens, podemos fazer um loop. Note que, se quisermos baixar os e-mails mais recentes primeiro, devemos começar do número mais alto até o número “1” (os servidores POP consideram “1” como o menor índice, e não “0” como estamos acostumados com o C#):
_emails.Clear(); int messageCount = client.GetMessageCount(); for (int i = messageCount; i > 0; i--) { var popEmail = client.GetMessage(i); }
Dentro da mensagem baixada pela biblioteca OpenPop.NET, temos basicamente duas versões da mensagem: uma em formato texto e a outra em formato HTML. Para recuperarmos a versão da mensagem em formato texto, utilizamos o método “FindFirstPlainTextVersion“. Já para baixarmos a versão HTML, utilizamos o método “FindFirstHtmlVersion“:
var popText = popEmail.FindFirstPlainTextVersion(); var popHtml = popEmail.FindFirstHtmlVersion(); string mailText = string.Empty; string mailHtml = string.Empty; if (popText != null) mailText = popText.GetBodyAsText(); if (popHtml != null) mailHtml = popHtml.GetBodyAsText();
Por fim, agora que já temos a mensagem tanto em formato texto quanto em formato HTML, podemos armazená-las dentro da nossa lista de e-mails (atributo chamado “_emails” no nível do formulário):
_emails.Add(new Email() { Id = popEmail.Headers.MessageId, Assunto = popEmail.Headers.Subject, De = popEmail.Headers.From.Address, Para = string.Join("; ", popEmail.Headers.To.Select(to => to.Address)), Data = popEmail.Headers.DateSent, ConteudoTexto = mailText, ConteudoHtml = !string.IsNullOrWhiteSpace(mailHtml) ? mailHtml : mailText });
Veja que, além das versões em texto e HTML, pegamos também algumas informações do cabeçalho (header) do e-mail, como remetente, destinatário, assunto, etc.
Confira a versão completa do código do evento “Click” do botão “Carregar“:
private void carregarButton_Click(object sender, EventArgs e) { try { Cursor = Cursors.WaitCursor; using (var client = new OpenPop.Pop3.Pop3Client()) { client.Connect(_hostname, _port, _useSsl); client.Authenticate(_username, _password); int messageCount = client.GetMessageCount(); _emails.Clear(); for (int i = messageCount; i > 0; i--) { var popEmail = client.GetMessage(i); var popText = popEmail.FindFirstPlainTextVersion(); var popHtml = popEmail.FindFirstHtmlVersion(); string mailText = string.Empty; string mailHtml = string.Empty; if (popText != null) mailText = popText.GetBodyAsText(); if (popHtml != null) mailHtml = popHtml.GetBodyAsText(); _emails.Add(new Email() { Id = popEmail.Headers.MessageId, Assunto = popEmail.Headers.Subject, De = popEmail.Headers.From.Address, Para = string.Join("; ", popEmail.Headers.To.Select(to => to.Address)), Data = popEmail.Headers.DateSent, ConteudoTexto = mailText, ConteudoHtml = !string.IsNullOrWhiteSpace(mailHtml) ? mailHtml : mailText }); } AtualizarDataBindings(); } } finally { Cursor = Cursors.Default; } }
Perceba que, além do código apresentado anteriormente, temos também um bloco “try-finally” para alterarmos o cursor para o modo “em trabalho” enquanto os e-mails são baixados, uma vez que esse processo pode demorar um bocado dependendo da quantidade de e-mails.
Além disso, note também que chamamos um método “AtualizarDataBindings” no final do processo. Esse método será responsável por atualizar as fontes de dados do ListBox e dos TextBoxes:
private void AtualizarDataBindings() { // Limpando os bindings. deTextBox.DataBindings.Clear(); paraTextBox.DataBindings.Clear(); dataDateTimePicker.DataBindings.Clear(); conteudoTextBox.DataBindings.Clear(); emailsListBox.DataSource = null; emailsBindingSource.DataSource = null; // Re-configurando os bindings. emailsBindingSource.DataSource = _emails; emailsListBox.DataSource = emailsBindingSource; emailsListBox.DisplayMember = "Assunto"; deTextBox.DataBindings.Add(new Binding("Text", emailsBindingSource, "De")); paraTextBox.DataBindings.Add(new Binding("Text", emailsBindingSource, "Para")); dataDateTimePicker.DataBindings.Add(new Binding("Value", emailsBindingSource, "Data")); conteudoTextBox.DataBindings.Add(new Binding("Text", emailsBindingSource, "ConteudoTexto")); }
Execute a aplicação e veja o resultado:
Exibindo a versão HTML dos e-mails
Muito bem, agora você já viu como exibir a versão em texto dos e-mails. Mas, lembra que nós recuperamos a versão HTML dos e-mails também? Pois bem, como é que fazemos para exibir a versão HTML dos e-mails? Simples, com um controle WebBrowser!
No lugar do “conteudoTextBox“, adicione um controle WebBrowser no formulário, dando o nome de “conteudoWebBrowser“. Feito isso, no método “AtualizarDataBindings“, adicione as linhas de código que farão o binding entre a propriedade “DocumentText” do controle WebBrowser com a propriedade “ConteudoHtml” dos e-mails que recuperamos anteriormente:
private void AtualizarDataBindings() { // Limpando os bindings. deTextBox.DataBindings.Clear(); paraTextBox.DataBindings.Clear(); dataDateTimePicker.DataBindings.Clear(); conteudoTextBox.DataBindings.Clear(); conteudoWebBrowser.DataBindings.Clear(); emailsListBox.DataSource = null; emailsBindingSource.DataSource = null; // Re-configurando os bindings. emailsBindingSource.DataSource = _emails; emailsListBox.DataSource = emailsBindingSource; emailsListBox.DisplayMember = "Assunto"; deTextBox.DataBindings.Add(new Binding("Text", emailsBindingSource, "De")); paraTextBox.DataBindings.Add(new Binding("Text", emailsBindingSource, "Para")); dataDateTimePicker.DataBindings.Add(new Binding("Value", emailsBindingSource, "Data")); conteudoTextBox.DataBindings.Add(new Binding("Text", emailsBindingSource, "ConteudoTexto")); conteudoWebBrowser.DataBindings.Add(new Binding("DocumentText", emailsBindingSource, "ConteudoHtml")); }
Execute novamente a aplicação e veja a diferença no resultado:
E o que mais poderíamos fazer?
Existem inúmeras outras coisas que poderíamos implementar nesse exemplo:
– Deletar e-mails do servidor
– Salvar os e-mails em um banco de dados e só carregar os que ainda não foram baixados
– Trabalhar com arquivos anexados
– Baixar somente o header do e-mail (ao invés do conteúdo completo, arquivos anexados, etc.)
Entretanto, para não deixar o artigo muito longo, vou parar por aqui. Dependendo da demanda, continuarei esse tópico em outros artigos no futuro. Enquanto isso, sugiro que você dê uma olhada na coleção de exemplos disponíveis no site da biblioteca OpenPop.NET:
OpenPop.NET – Examples Overview
Concluindo
Ao falarmos de recebimento de e-mails, temos duas opções de protocolo: POP3 ou IMAP. O protocolo POP3 é mais limitado, mas, ainda é muito utilizado hoje em dia e simplesmente qualquer servidor de e-mails implementa esse protocolo.
Nesse artigo você viu as particularidades do protocolo POP3 e as particularidades extras da implementação do POP3 do Gmail. Além disso, apresentei uma lista de bibliotecas comerciais para a implementação de POP3 e IMAP em aplicações .NET e mostrei também como utilizar a biblioteca OpenPop.NET (que é open source) para baixarmos e-mails utilizando o protocolo POP3, tanto em sua versão texto como em sua versão HTML.
Agora pergunto: você já teve que implementar o recebimento de e-mail em alguma aplicação que você desenvolveu? Caso positivo, qual dos protocolos você utilizou, POP3 ou IMAP? Qual foi a biblioteca que você acabou utilizando? E, caso você ainda não tenha implementado o recebimento de e-mails, conte nos comentários o que você achou dessa opção de recebimento de e-mails com a biblioteca OpenPop.NET.
Por fim, convido você a inscrever-se na minha newsletter. Ao fazer isso, você receberá um e-mail toda semana sobre o artigo publicado e ficará sabendo também em primeira mão sobre o artigo da próxima semana, além de receber dicas “bônus” que eu só compartilho por e-mail. Inscreva-se utilizando o formulário logo abaixo.
Até a próxima!
André Lima
Photo by Pixabay used under Creative Commons
https://pixabay.com/en/letters-email-newsletter-leave-1132703/
https://pixabay.com/en/email-keyboard-computer-mail-at-824310/
Crystal Reports parou de funcionar depois de atualizar para o Windows 10. O que fazer? Gerando relatórios do Report Viewer com Entity Framework
Boa André!!
Mais uma vez arrebentou, obrigado….
Olá Eduardo, obrigado pelo comentário! Fico feliz que você tenha gostado..
Abraço!
André Lima
Então André. Estou utilizando o OpenPop3. Corri vários sites, inclusive do OpenPop3 e não encontro uma solução. Aqui na sua rotina, quando clico em carregar, ele carrega os recebidos, porem traz tb os enviados. Eliminei as linhas onde definem Para, mas não adiantou. Utilizo o Gmail. Alguma solução para meu problema?
Olá Wil.
O problema não está na rotina, mas sim no jeito que o Gmail implementa o protocolo POP.. Ele retorna também os e-mail que foram enviados, como se eles tivessem sido recebidos.. Se você linkar a sua conta do Gmail no Outlook, enviar um e-mail pelo site e depois sincronizar no Outlook, você verá que o e-mail enviado será sincronizado com o Outlook como se ele tivesse sido enviado..
Para contornar isso, você precisaria analisar o header da mensagem (mais especificamente o campo “From”) para ver se o e-mail não foi enviado pela conta que você está sincronizando..
Abraço!
André Lima
Então caro André. Como minha ideia é armazenar em um BD as mensagens tanto enviadas como recebidas comecei pelas recebidas. Notei que o RECENTE:USERNAME é o responsável por trazer as enviadas tb.
Logo, retirei o Recente, assim aparecem apenas as recebidas NÂO LIDAS. O fluxo do email no sistema é direcionado para ele, logo, não haverá a necessidade de abrir um webmail ou um cliente de e-mail.
Os dados serão armazenados no bd e depois mostrados através de uma DataGriedView + um DataTable(segui o modelo do Thunderbird)Segui. ASSUNTO – DE – DATA e com um RichText para o corpo da mensagem. Ao carregar o e-mail, ja excluo do servidor. Ficou show (desculpe a metidez..rs)…Segue o código.
try
{
using (var client = new OpenPop.Pop3.Pop3Client())
{
client.Connect(_hostname, _port, _useSsl);
client.Authenticate(_username, _password);
int messageCount = client.GetMessageCount();
for (int i = messageCount; i > 0; i–)
{
var popEmail = client.GetMessage(i);
var popText = popEmail.FindFirstPlainTextVersion();
var popHtml = popEmail.FindFirstHtmlVersion();
string mailText = string.Empty;
if (popText != null)
mailText = popText.GetBodyAsText();
if (popHtml != null)
mailText = popHtml.GetBodyAsText();
string vemail1, vnome1, vassunto1, vmensagem1;
DateTime vdata1;
vnome1 = popEmail.Headers.From.DisplayName;
vemail1 = popEmail.Headers.From.Address;
vassunto1 = popEmail.Headers.Subject;
vdata1 = popEmail.Headers.DateSent;
vmensagem1 = mailText;
…. salva no BD, atualiza o TableAdapter e exibe o DataGried
Olá Wil!
Que legal, que bom que deu certo.. :)
Obrigado por ter enviado o código.. Com certeza pode ser útil para outras pessoas que precisarem fazer a mesma coisa..
Abraço!
André Lima
O sistema é fechado para a carteira de clientes, logo e-mails só poderão ser enviados para clientes cadastrados.
Há um cadastro no sistema para username e senha cadastráveis no sistema de acordo com a necessidade do usuário. Não encontrava essa solução para emails no Delphi, por isso migrei pro VS. É um sistema jurídico de cobrança de clientes.
Abaixo a explicação como fiz.
Grande abraço.
Entendi, Wil.. Valeu mais uma vez pela explicação detalhada..
Abraço!
André Lima
Muito bom o artigo. Parabéns!
Ansioso para o artigo sobre o imap.
Olá Daniel, obrigado pelo comentário! Fico feliz que você tenha gostado..
Vamos ver se eu consigo publicar o artigo sobre o IMAP no segundo semestre desse ano..
Abraço!
André Lima
Ótimo post André!
Caso eu queira fazer o mesmo utilizando o provedor da Microsoft (Outlook.com) qual o link de hostName devo utilizar?
Obrigado.
Olá José, obrigado pelo comentário!
As informações de configuração do POP3 do Outlook.com você encontra aqui:
POP3 and IMAP settings for Hotmail and Windows Live Mail
Testa aí e depois fala pra gente se funcionou..
Abraço!
André Lima
bom dia andre,
estive procurando algo deste tipo já algum tempo.
fiz conforme acima e obtenho um erro que é o seguinte;
“Server did not accept user credentials”
as credenciais esta corretas de acordo com o gmail.
tens alguma dica?
obrigado.
Olá Antonio!
Já respondi a sua questão lá na thread original criada nos fóruns da MSDN.. Vou colar a resposta aqui também só para documentar, caso alguém passe pelo mesmo problema..
Esse erro vindo do Gmail normalmente acontece por dois motivos:
1) O POP3 não está habilitado nas configurações do Gmail (eu detalhei esse ponto no artigo).. Habilite em Configurations / Forwarding / POP
2) O acesso à sua conta do Gmail não está habilitado para aplicativos “menos seguros”.. Altere essa configuração no link https://www.google.com/settings/security/lesssecureapps
Por favor, confira essas duas configurações e tente novamente..
Abraço!
André Lima
fiz exatament com vc fez , porem essa palavra da erro, fica em vermelho
emailsBindingSource.
o que fiz de errado??
abraços
Olá Afonso!
Provavelmente você esqueceu de adicionar ou renomear o Binding Source do formulário.. Veja só, vou colocar aqui embaixo a parte do artigo onde eu falo sobre a criação do Binding Source:
Abraço!
André Lima
Ótimo post.
Recentemente precisei desenvolver uma aplicação para carregar a caixa de entrada de 4 servidores de e-mail diferentes e utilizeio OpenPop.
No momento estou pesquisando sobre como carregar os itens que vão para o lixo eletrônico ou para a caixa de span. Alguém tem alguma solução em C# para me indicar?
Olá Camila, obrigado pelo comentário!
Infelizmente, o protocolo POP3 não tem o conceito de “pastas”, ou seja, ele só dá a possibilidade de lermos a caixa de entrada.. Para acessar estrutura de pastas (como as pastas de spam ou lixo eletrônico), você terá que utilizar o protocolo IMAP..
Abraço!
André Lima
Bom dia André Alves, obrigada pela resposta.
Consegui desenvolver a parte que lê a caixa de spam usando o protocolo IMAP, com a biblioteca Koolwired.IMAP. Pra quem precisar, é uma biblioteca open source, não encontrei nenhum tipo de documentação e também não encontrei ela compilada (DLL). Tive que baixar o projeto Kollwired inteiro na minha máquina, compilar e fazer referência no meu projeto.
Sobre a falta da documentação, você realmente consegue se adaptar facilmente com os overloads e nome dos métodos.
Foi uma busca e tanto, mas consegui!
De novo, obrigada pela dica.
Olá Camila, obrigado pelo retorno! Vou anotar aqui o nome dessa biblioteca para quando eu for escrever o artigo sobre IMAP.. Muito obrigado pela indicação! :)
Abraço!
André Lima
Valeu André ajudou 100%!
Olá Anisio, obrigado pelo comentário! Fico feliz que você tenha gostado!
Abraço!
André Lima
Ola André
Eu uso o Openpop faz uns 2 anos, mas agora a microsoft esta mudando o padrão de criptografia do POP para TLS, e nesse padrão o sistema não esta mais recebendo os email, sabe se tem alguma atualização para isso ou outro componente que já esteja pronto para essa mudança;
Olá Giovani!
De qual provedor você está tentando baixar e-mails? Do Outlook.com? Como é que está o seu código? Teoricamente a biblioteca OpenPop suporta TLS também..
Abraço!
André Lima
Well… meio antigo o post, mas acabou surgindo uma necessidade aqui na empresa de capturar arquivos que são anexados aos e-mails… Estava procurando algo free e acabei caindo nessa sua publicação! E acho muito válido dar um retorno. Foi de grande ajuda esse seu post, eu acabei usando o OpenPop, pois só necessito entrar em uma conta de e-mail e baixar os arquivos anexados para uma pasta. Não segui seu post a risca, mas ele me ajudou muito na minha tarefa! Obrigado, vou dar uma passada por todo seu blog, parece bem interessante!
Olá Fabiano, muito obrigado pelo comentário! Fico feliz que o tutorial tenha te ajudado.. :)
Depois que você der uma olhada nos outros artigos, se ficar com alguma dúvida ou se tiver alguma sugestão para outros temas, é só avisar..
Abraço!
André Lima
Consegui fazer funcionar perfeito, porem agora eu preciso pegar o xml q ta anexado no email e salvar no c:/xml
Olá Bruno!
Nesta thread do StackOverflow você encontra alguns códigos demonstrando como recuperar os arquivos anexados através do OpenPop.NET:
How to save email attachment using OpenPop
Abraço!
André Lima
Certo eu usei o exemplo deu certo, porem eu quero que ele baixe apenas anexo com extensão xml por exemplo se eu colocar if (attachment.ContentId.Equals (“teste.xml”)) ele só vai baixar o xml que se chamar teste.xml, outro xml com nome diferente não baixa, nem que eu deixe somente “xml” ele so baixa se for igual mesmo. Consegue me ajudar
Olá Bruno!
Você pode utilizar “Contains” ao invés de utilizar o “Equals”, passando somente a extensão (attachment.ContentId.Contains(“.xml”)), aí ele vai funcionar para qualquer arquivo xml que você receber..
Abraço!
André Lima
André deu certo! muito obrigado.
Porem surgiu mais uma duvida, ele so pega um unico xml no email, se o email vem com dois ele ja no pega, meu código final esta assim
try
{
client.Connect(“SMTP”, 110, false); //UseSSL true or false
client.Authenticate(“USUARIO”, “SENHA”);
var messageCount = client.GetMessageCount();
var Messages = new List(messageCount);
var contdel = messageCount;
progressBar1.Maximum = messageCount +1;
//MessageBox.Show(“Existem ” + contdel.ToString() + “para receber!”);
for (int i = 0; i < messageCount; i++)
{
OpenPop.Mime.Message getMessage = client.GetMessage(i + 1);
Messages.Add(getMessage);
progressBar1.Value= i+1;
label1.Text = ("Recebendo" + i.ToString() + "Mensagens");
}
foreach (OpenPop.Mime.Message msg in Messages)
{
foreach (var attachment in msg.FindAllAttachments())
{
string filePath = Path.Combine("c:\\anexo\\", attachment.FileName);
if (attachment.FileName.Contains("xml"))
{
FileStream Stream = new FileStream(filePath, FileMode.Create);
BinaryWriter BinaryStream = new BinaryWriter(Stream);
BinaryStream.Write(attachment.Body);
BinaryStream.Close();
}
}
}
Olá Bruno!
Que coisa estranha hein.. O seu código está aparentemente correto, com o foreach para checar todos os attachments da mensagem.. Você já tentou colocar um breakpoint ali no foreach e debugar para tentar entender o que está acontecendo?
Abraço!
André Lima
Andre, exclui meu comentario.. esqueci e passei o email e senha no codigo
Olá Bruno!
Fica tranquilo.. Eu já tinha percebido e tinha editado o seu comentário removendo essas informações do código que você postou..
Abraço!
André Lima
André, estou com um problema não sei se pode me ajudar, tem momentos que chegam lotes de emails, tipo 150 numa vez só, e quando acontece isso simplesmente ele para de receber, sem nenhum erro aparente. A pergunta é, o openpop tem limite de quantidade para baixar? ou é porque eu rodo a busca com timer de 1 minuto, e ele não concluiu a primeira busca e exclusão e startou outra na sequencia?
Olá Bruno!
Que eu saiba o OpenPOP não tem limite nenhum.. Ele vai te retornar todas as mensagens que você pediu que ele baixasse (as ainda não processadas).. Porém, o protocolo POP só marca as mensagens como “enviadas” (de forma que elas não sejam retornadas na próxima sincronização) quando o recebimento é feito com sucesso.. Nesse caso, acredito que os 60 segundos de intervalo não estão sendo suficientes e a sua aplicação está entrando em loop infinito..
O ideal nesse caso é só processar os e-mail se você ainda não estiver dentro de um loop de processamento.. Sugiro que você adicione esse tratamento na sua aplicação, caso ainda não tenha feito..
Abraço!
André Lima
Andre apos minunciosos testes descobri a causa, so nao sei a soluçao
Quando o nome do anexo tem mais que 50 caracter ele trava e fica ali tentando receber. Alguma sugestão?
Olá Bruno!
Que coisa hein.. Como é que ficou o seu código final? Você poderia compartilhar para eu tentar testar aqui?
Abraço!
André Lima
André, demorei para responder pois estava testando efetivamente, encontrei o problema, um erro primário, depois da leitura eu não fechava a conexão com o pop, abrindo inúmeras, que chegava em um momento que parava de receber.
Apenas fiz um if para testar se estava conectado fechar a conexão.
Muito obrigado pelo apoio, abaixo código final pode ajudar alguém.
public void VerificarEmails()
{
while (true)
{
Thread.Sleep(100000);
var cliente = new Pop3Client();
var num = 0;
//Arquiva xml de notas
try
{
Pop3Client client = new Pop3Client();
client.Connect(“pop.servidor.com.br”, 110, false);
client.Authenticate(“email@servidor.com.br, senha”);
List _messages = new List();
int messageCount = client.GetMessageCount();
int contdel = messageCount;
for (num = messageCount; num > 0; num–)
{
_messages.Add(client.GetMessage(num));
}
string _path = @”C:\doc\nfet”;
foreach (OpenPop.Mime.Message message in _messages)
{
foreach (MessagePart attachment in message.FindAllAttachments())
{
string filePath = Path.Combine(_path, attachment.FileName);
if (Path.GetExtension(filePath) == “.xml”)
{
FileStream Stream = new FileStream(filePath, FileMode.Create);
BinaryWriter BinaryStream = new BinaryWriter(Stream);
BinaryStream.Write(attachment.Body);
Stream.Close();
BinaryStream.Close();
}
}
}
}
for (int l = contdel; l > 0; l–)
{
client.DeleteMessage(l);
}
if (client.Connected)
client.Disconnect();
}
catch (Exception ex)
{
Console.WriteLine(“”, ex.Message);
}
finally
{
if (cliente.Connected)
cliente.Disconnect();
}
Olá Bruno, muito obrigado pelo retorno!
Desenvolvimento de software é assim mesmo.. Muitas vezes ficamos analisando um problema por dias achando que é uma coisa, e no final das contas é outra coisa completamente diferente que estava bem na nossa cara o tempo todo.. :)
Obrigado por ter compartilhado o código final!
Abraço!
André Lima
Andre, estou com um caso muito estranho, tem alguns emails que trava minha rotina, com a exceção “Caracteres inválidos no caminho.”, ai se eu leio o anexo.FileName.ToString() que seria o arquivo ele esta lendo assim /* “NF-e 00000000000000000000000000000000000000000000.xml”Content-Disposition: attachment */no outlook abro normal o anexo, só o openpop q faz isso, alguma sugestão?
Olá Bruno!
Que estranho hein.. Não faço ideia do que possa estar acontecendo.. Provavelmente é alguma coisa inválida no jeito que o anexo foi atachado nessa mensagem que está caindo em um bug do OpenPop e que o Outlook mesmo assim consegue interpretar..
Você não conseguiu detectar alguma semelhança entre esses e-mails que travam a rotina? Eles vêm sempre do mesmo remetente? Ou de remetentes diferentes? Se você executa a rotina novamente, o e-mail é processado? Ou você tem que remove-lo na mão para que a rotina volte a funcionar?
Abraço!
André Lima
Então, por enquanto eu peguei de um único remetente, analisei a estrutura original do email olha só a parte do anexo “Content-Type: application/xml; name=”NF-e CHAVE_DA_NFE.xml”Content-Disposition: attachment; filename=”NF-e CHAVE_DA_NFE.xml”
Content-Transfer-Encoding: base64
” sendo que nos outros a estrutura vem so filename e o nome certinho, e nesse caso pelo que percebi ele le o name e nao o filename que tem o valor correto.
Olá Bruno!
Que zica hein.. E o “attachment.Body” está correto? Ele tem o conteúdo do e-mail ou o OpenPop não está conseguindo salvar também? Ou seja, você colocar outro nome para salvar o arquivo, ele salva o conteúdo?
Se o conteúdo do arquivo estiver correto no “Body”, você poderia fazer um tratamento.. Quando ocorrer essa exception, você tenta extrair o nome do arquivo correto que está ali no meio do nome do arquivo.. É meio gambiarra, mas não vejo outra maneira (que não seja 1 – depurar o código do OpenPop para entender o porquê de ele não estar fazendo o parse correto do nome do arquivo nesse caso; ou 2 – tentar trocar de biblioteca)..
Abraço!
André Lima
Apenas altera a chave xml que postei no meu comentário por segurança.
Tranquilo, Bruno.. Eu alterei antes de aprovar o seu comentário..
Abraço!
André Lima
Então, acho que eu tive uma ideia, não sei como por em pratica, quando eu leio no filename (“NF-e 00000000000000000000000000000000000000000000.xml”Content-Disposition: attachment)ele vem o nome do arquivo correto entre aspas, tem como eu mandar ler só o que esta dentro das aspas? se tiver creio que resolve meu problema.
Olá Bruno!
Com Regular Expressions você consegue extrair facilmente o conteúdo que está entre as aspas duplas.. Tem um exemplo parecido nesta thread do StackOverflow, veja só:
Regex: C# extract text within double quotes
Abraço!
André Lima
Olá André preciso de uma ajuda,
fiz exatamente o que passou a cima no c# porém estou com erro numa linha do código que nao estou conseguindo achar solução.
Erro nessa linha :
Para = string.Join(“; “, popEmail.Headers.To.Select(to => to.Address)),
ele me sugere que o melhor método para usar seria dessa forma ‘string.Join(string, string [])’ e que tem argumentos inválidos.
desde já grato pelo apoio
André consegui resolver, coloquei um .ToArray() no final da linha e foi, voltou a mensagem, tudo perfeito.
Porém preciso pegar o anexo das mensagens também e já procurei na internet e não achei nada que me ajudasse. Por acaso você teria um artigo para me indicar sobre? que me ajudasse. Queria linkar com o seu exemplo, pois tive um compreensão maior seguindo o processo que fez.
Desde já agradeço!
Olá Erick!
Que bom que você conseguiu resolver o problema do outro comentário.. Quanto ao esquema de anexo, tem uma thread no StackOverflow com um exemplo disso utilizando essa mesma biblioteca que eu utilizei no artigo.. Veja se te ajuda:
How to save email attachment using OpenPop
Abraço!
André Lima
Fala André, dei uma olhada no artigo, porém tive um entendimento mediano de tudo o que tem la, (ainda sou leigo na programação, mas to me desenvolvendo kkk)… vou te passar o porque que eu precisava do anexo do email, seria apenas para puxar XML de nota que cliente irá enviar para a empresa na qual trabalho, e precisava jogar esses XML para uma pasta. Consegue me ajudar com isso?? vou tentando puxar a partir do artigo que me passou mas to bem perdido no como puxar. Agradeço a ajuda desde já e volto a repetir a sua publicação ficou totalmente didática e me fez compreender muito bem o que é o pop e como funciona.
Olá Erick!
Não é muito difícil recuperar os anexos das mensagens.. Montei um exemplo bem simples aqui que percorre as mensagens e salva os anexos de todas as mensagens no diretório da aplicação:
Claro que você terá que adaptar para o seu cenário, mas já é um começo.. Se ficar com alguma dúvida, é só falar..
Abraço!
André Lima
Amigo, a principio fiquei apreensivo quanto a funcionalidade desse código.
Depois de buscar incansavelmente por exemplos com códigos mais curtos, me rendi ao seu trabalho, que por sinal está ótimo. Parabéns! O código funciona perfeitamente.
Porém ainda há muitas dúvidas quanto aos métodos da classe OpenPop, como por exemplo: salvar anexos em disco.
Estou tentando aqui, mas sem sucesso.
Pode me ajudar nisso.
Grande abraço e sucesso em sua carreira.
Olá Alex, muito obrigado pelo comentário!
Quanto ao esquema dos anexos, eu acabei de responder um comentário aqui mesmo sobre esse assunto.. Preparei um exemplo bem simples que percorre as mensagens e salva os anexos no diretório da aplicação.. Claro que você terá que adaptar para o seu cenário, mas já dá para ter uma ideia de como implementar essa funcionalidade..
Veja o meu comentário neste link..
Abraço!
André Lima
Fala André, cara passando aqui para agradecer pelo apoio e dedicação ao seu site e a quem te segue/ admira… você me ajudou demais, consegui com o artigo e o ultimo exemplo puxar os anexos dos emails, vi aonde ele esta jogando porém ainda falta puxar o que vem para outra pasta na máquina, eu preciso criar alguma conexão? ou consigo apenas passar o caminho da onde jogo o retorno dos anexos puxados? aguardo novamente sua resposta e cara, que ajuda imensa você me deu. Continuo tentando puxar os arquivos e queria deixar ele puxando apenas .xml. Consigo pelo filename do exemplo que postou?
Olá Erick!
Magina cara, sem problema.. :)
Quanto à sua dúvida, você consegue especificar a pasta na hora de criar o FileStream (é o primeiro parâmetro do construtor dele).. Ao invés de passar o anexo.FileName, você passa o caminho com o diretório onde o arquivo deve ser salvo..
Já quanto ao filtro para arquivos xml, você pode pegar a extensão do anexo e só continuar com a execução caso a extensão seja XML..
Seria algo mais ou menos assim (não testei):
Abraço!
André Lima
Fala André, passando aqui, mais que atrasado, para agradecer ao apoio que me deu no retorno dos anexos pelo pop… cara você me ajudou em um tanto que não tem noção, foi atencioso, gentil, humilde, parabéns pelo profissional e pela pessoa que é!
Hoje tenho um novo desafio e irei pedir sua ajuda estou precisando trazer um relatório de um view do sql em mvc e após trazer isso clicar no grid (que retornara os dados) e abrir referente a linha clicada mais detalhe, consegue me indicar um artigo que ajude nesse quesito, passo a passo para retornar dados na tela com mvc, um grid interligado…enfim já foi e será de grande ajuda a sua atenção nesses casos! novamente Obrigado e um abraço cara!
Olá Erick!
Não precisa agradecer, fico feliz por ter conseguido ajudar.. :)
Quanto à sua dúvida do grid com MVC, infelizmente eu não tenho praticamente nenhuma experiência com desenvolvimento web.. Vou ficar devendo.. :(
Mas, eu sugiro que você procure por “master detail mvc sql server” que provavelmente você vai acabar encontrando algo nessa linha.. Como, por exemplo, este artigo que eu encontrei no CodeProject:
Master Detail Grid in Asp.net Mvc5
Abraço!
André Lima
André, irei dar uma olhada cara!! como sempre ajudando e muito.
Voltando ao pop kkkk…queria saber se à uma maneira de eu mover as mensagem já lidas pelo programinha criado, para outra pasta, tipo um Backup, para que na proxima lida do programa não tenha que ler aquilo novamente.
Novamente fico grato desde já pela ajuda, se um dia eu puder retribuir a força que esta me dando, pode ter certeza que retribuirei!! Vlw André
Olá Erick!
Você poderia salvar o conteúdo do e-mail em arquivos, ou até mesmo em um banco de dados, utilizando o ID da mensagem como “chave” para o e-mail que foi salvo.. Aí, ao invés de você carregar todas as mensagens direto, você carrega só o header e verifica se a mensagem já não foi baixada anteriormente..
Entendeu a ideia? Você já pensou em fazer dessa maneira?
Abraço!
André Lima
Fala André, beleza? então eu entendi mais ou menos a ideia que me passou, tipo quando o programa puxa os anexos dos emails ele joga para uma pasta onde um outro processo aqui da empresa(tipo um robô), vai ler eles para integrar com o sistema(os anexos .xml) dai mesmo assim o email continua lá! O header seria o processo de leitura dos emails? como eu carregaria do header a mensagem? não pensei nisso antes, pois não fazia ideia de como faria para não puxar os outros emails, sem passar tudo denovo… aguardo resposta
Abraço André!!
Olá Erick!
Se você seguiu o exemplo do meu artigo, provavelmente você está utilizando o método “GetMessage” direto para baixar o e-mail, correto? Pois bem, existe uma maneira de só baixar os headers dos e-mails (ou seja, só o assunto, remetente, etc), o que é um processo bem leve.. Tem um exemplo desta funcionalidade neste link:
OpenPop.NET – How to examine headers
Dentro do header, você tem o “ID” da mensagem (propriedade MessageId).. Se você sempre salvar em algum lugar uma lista dos “MessageIds” que já foram baixados anteriormente, você pode desconsiderar estes e-mails (não chamando o “GetMessage” para eles, que é a parte mais pesada e que fará com que a mensagem completa seja baixada do servidor)..
Ficou mais claro? Se não ficou, me avisa que eu tento explicar de outra maneira..
Abraço!
André Lima
Entendi sim André, porém tem outra coisa que tava pensando e que lembrei pelo post seu que não da pra fazer com o protocolo POP e acho que seria o ideal para fazer que seria ler e jogar as mensagens lidas para uma pasta no próprio email(seja criando a pasta ou simplesmente movendo os ja lidos)…e isso é só com o imap, correto?? se sim cara, é muito diferente do POP e consegue me indicar algum artigo para eu seguir como exemplo, vi que esta como sugestão de temas para proximos artigos seu porém preciso resolver isso. consegue me dar uma força quanto à isso?
Abraço cara!! vlw pela ajuda!
Olá Erick!
Realmente o protocolo POP não tem suporte a pastas, a única saída seria partir para IMAP mesmo.. Eu ainda não implementei nenhum exemplo aqui com IMAP, mas quando pesquisei sobre esse assunto, eu encontrei uma biblioteca chamada Koolwired.IMAP.. Porém, como você pode ver, aparentemente essa biblioteca está abandonada..
Aproveitei o seu comentário para dar uma pesquisada melhor no GitHub e acabei encontrando uma outra biblioteca com suporte a IMAP.. Acho que vale a pena dar uma investigada:
MailKit
Abraço!
André Lima
Já estou dando uma olhada André, surgiu uma outra prioridade aonde trabalho, que é salvar um arquivo .pdf no sql server! dai um procurada aqui no seu site tem exemplos com PDF porém nenhum salvando no Banco de dados, tem alguma noção de como fazer isso? artigo, bilbioteca a usar e até mesmo que campo usaria no Banco para armazenar o PDF. Detalhe, eu puxaria o arquivo de uma pasta, ai pelo Forms eu abriria um dialogo com o explorer do windows para achar o arquivo e importaria para o sql server, isso é para anexar o arquivo de licença a um cadastro de licença do transportador (apenas para controle total da operação). Desde já obrigado André!!
Abraçooo
Olá Erick!
Você pode salvar arquivos PDF como qualquer outro arquivo no banco de dados.. O tipo da coluna varia de banco de dados para banco de dados.. No SQL Server, por exemplo, você poderia utilizar um campo IMAGE..
Eu tenho um artigo/vídeo mostrando como salvar imagens no banco de dados.. Você pode se basear nele para salvar os arquivos PDF também:
Salvando imagens no banco de dados utilizando C#
Abraço!
André Lima