4 de novembro de 2011

Trabalhando com emails em uma caixa postal usando IMAP4 em Delphi

Mostrei num outro post aqui no blog como ter acesso aos emails numa caixa postal através do protocolo POP3 no Delphi. O POP3 é um protocolo amplamente adotado para permitir o acesso remoto aos emails de uma caixa postal mas ele é bastante limitado. Seus recursos estão restritos basicamente à manipulação das mensagens existentes na caixa postal, permitindo lê-las, apagá-las ou arquivá-las.

Se precisar de recursos mais avançados - tais como gerenciar pastas, localizar uma mensagem pesquisando o texto dela, compartilhar caixas postais com um grupo de trabalho, entre outras - temos que apelar para o IMAP4, um protocolo mais completo.

O primeiro passo para trabalhar com o IMAP4, assim como acontece com os outros protocolos baseados no TCP/IP, é estabelecer uma conexão com o servidor. Aqui também isso exige que alguns parâmetros sejam informados, tais como o endereço do servidor, a porta onde ele estará aguardando requisições e um usuário com permissão de acesso ao serviço, bem como sua respectiva senha:
var iMap TIdIMAP4;
begin
iMap := TIdIMAP4.Create (Nil);

{ Atribui os dados fornecidos pelo usuário e tenta se conectar }
iMap.Host := _ServerIMAP; { Endereço do servidor }
iMap.Port := _PortaIMAP; { Porta que o servidor está ouvindo }
iMap.Username := _UserIMAP;
iMap.Password := _SenhaIMAP;

iMap.Connect(100000);

{ Realiza aqui algumas tarefas com o IMAP conectado}

{ Descarta a conexão e outros recursos qdo não forem mais necessários }
iMap.Disconnect();

iMap.Free ();
end;
Uma grande diferença em relação ao funcionamento do POP3 é que o IMAP4 pode gerenciar várias caixas postais numa única conexão. Do ponto de vista do usuário, cada caixa postal é normalmente encarada como uma pasta. Como os comandos do IMAP4 são direcionados a uma caixa postal, isso nos obriga a selecionar uma antes de submeter os comandos.

Ao menos a caixa chamada INBOX (caixa de entrada) deve existir mas você pode levantar uma lista completa com todos os nomes das caixas que existem na conta usando a função ListMailBoxes. Um usuário pode, então, ser apresentado a essa lista para que escolha a caixa mais apropriada para as necessidades dele. Considerando o iMap criado e conectado no quadro anterior, fazer o levantamento é bem simples:
var lMaixBoxes : TStringList;
lCaixaSel : String;
begin
{ ... }
lMaixBoxes := TStringList.Create();

iMap.ListMailBoxes(lMaixBoxes);

{ Carrega a lista na tela para o usuário escolher uma delas. }
lCaixaSel := SelecionaCaixaPostal (lMaixBoxes);

lMailBoxes.Free();

if (not iMap.SelectMailBox (lCaixaSel) ) then
raise Exception.Create('Erro na seleção da caixa postal');
{ ... }
end;
Uma vez que a caixa foi escolhida, precisamos repassá-la ao iMap para que possamos operar com a caixa específica. É o que faz a chamada à função SelectMailBox no código acima. Essa chamada faz com que o TidIMAP4 alimente sua propriedade MailBox com um resumo do estado atual da caixa selecionada, disponibilizando informações como a quantidade total de mensagens e quantas dessas permanecem não lidas, entre outras informações.

Com uma caixa selecionada, podemos realizar operações como pesquisar os emails contidos nela. Nesse quesito, o IMAP4 é muito mais flexível que o POP3 já que nos fornece uma opção para filtrar as mensagens desejadas usando a estrutura TIdIMAP4SearchRec. O principal recurso dessa estrutura é o campo SearchKey. É esse campo que determina quais critérios devem ser respeitados na consulta e, portanto, quais dos outros campos da estrutura serão considerados quando a consulta for submetida pela função SearchMailBox. O exemplo abaixo usa o iMap conectado para procurar todas as mensagens que ainda não foram lidas na caixa postal atual e varre a lista encontrada, recuperando cada uma delas.
var rec : array of TIdIMAP4SearchRec;
TheMsg : TIdMessage;
contMsg, i, idMsg : Integer;
begin
{ Procura por emails do tipo skUnseen - ou seja, somente os "não lidos" }
SetLength(rec,1);
rec[0].SearchKey := skUnseen;
if not iMap.SearchMailBox (rec) then
raise Exception.Create('Erro na pesquisa da caixa postal');

{ Quantas mensagens foram encontradas ? }
contMsg := iMap.MailBox.SearchResult.Length;

for i := 0 to contMsg - 1 do begin
TheMsg := TIdMessage.Create(Nil);

{ Obtem a identificação da mensagem ... }
idMsg := iMap.MailBox.SearchResult[i];

{ ... e a usa para recuperar o email completo }
iMap.Retrieve (idMsg, TheMsg);

{ Analisa anexos e outros procedimentos }
end;
end;
Veja que a a função de pesquisa aceita um array de filtros como parâmetro. Isso significa que podemos construir uma combinação de filtros para que uma lista mais refinada seja retornada. Outras possibilidades de filtros para pesquisar a caixa incluem o texto contido nas mensagens ou seus cabeçalhos, a data de recepção delas ou até mesmo seu tamanho.

Um outro ponto importante a destacar é que o resultado da pesquisa é armazenado na propriedade SearchResult do MailBox. O resultado fica acessível como uma lista de identificadores das mensagens que atenderam o critério estipulado para a busca. Esses identificadores são números inteiros que podem ser usados em diversas funções da caixa postal, como a que recupera o conteúdo da mensagem ou que permite excluí-la.

Uma vez que uma mensagem foi recuperada com sucesso, a instância da classe TIdMessage nos dá acesso a todas as informações pertinentes a ela, incluindo os anexos, caso ela possua algum. O trecho que trata os anexos foi removido pra não poluir muito o quadro. Como a recuperação de anexos funciona exatamente igual ao do POP3, o processo descrito no post Recuperando emails de uma caixa postal com Delphi pode ser usado para realizar esse trabalho.

Um outro recurso do IMAP4 é que você pode copiar mensagens da caixa atualmente selecionada pra uma outra caixa usando a função CopyMsgs. Ela aceita 2 parâmetros : um array com os identificadores das mensagens a serem copiadas e o nome da caixa postal que receberá as mensagens. Caso precise mover as mensagens, use essa função combinada com a DeleteMsgs, que exclui uma lista de mensagens da caixa atual.

O IMAP4 ainda disponibiliza funções para gerenciar as caixas postais. É possível criar uma caixa nova, renomer uma caixa e excluir caixas existentes. Os nomes das funções com esses objetivos são bastante intuitivos : CreateMailBox, DeleteMailBox e RenameMailBox.