<-- Capítulo

Índice del tutor de Delphi
© Copyright 1999
por David Martínez.

Todos los derechos reservados

Capítulo -->

Capitulo 10.4. Programando un Lector de E-Mail (POP3/SMTP)

Todos hemos usado un lector de correo electrónico. Tecleamos nuestro nombre de usuario y password y el lector se conecta a nuestro servidor POP3 y baja los mensajes.

Nota: Como vimos en el capítulo anterior, también existe el protocolo IMAP, que nos permite mantener los mensajes en el servidor. Delphi 4 no tiene componentes para manejar protocolos IMAP. Pero usted podría hacer el suyo utilizando la especificación RFC de IMAP y el capítulo acerca de comunicación por sockets.

Archivando Nuestros Correos

Un lector de e-mail POP requiere de dos partes. La primera es como leer y escribir el correo a disco. Usted puede utilizar cualquier formato que desee (una base de datos, un archivo de texto, etcétera).

Un correo electrónico, como elemento de datos, es muy sencillo. Un correo se compone de un texto y una serie de atributos con valores. Estos atributos son llamados "Headers", cabeceras o encabezados. Dentro de los encabezados el correo nos da toda clase de información, desde la fecha y hora del correo, quien lo envía y el título, hasta la ruta desde la computadora de origen hasta la computadora de destino.

¿Guardar en Tablas o No en Tablas? He ahí el Dilema

Si usted va a hacer una base de datos para guardar correo, simplemente necesita tres tablas.

La primera tabla tendrá un identificador y los campos más comunes que quiera hacer disponibles en la vista inicial (From, Subject, Date, etcétera). La segunda tabla es igual de sencilla. El mismo identificador, más un campo MEMO con el texto del mensaje. La tercera tabla contendrá un identificador (que podrá ser repetido), mas un set de nombres y valores de atributos. He aquí un diseño básico para tablas de e-mail:


Tabla: Atributos

Campo      Tipo(Longitud)  Atr
-----------------------------------
CorreoID   LongInt        NOT NULL
Atributo   String(80)     NOT NULL
- - - - - - - - - - - - - - - - - - (Campos Llave)
Valor      String(255)    NULL

Tabla: CorreoTexto

Campo      Tipo(Longitud)  Atr
-----------------------------------
CorreoID   LongInt       NOT NULL
- - - - - - - - - - - - - - - - - - (Campos Llave)
Texto      Memo          NULL

Tabla: CorreoVista
-----------------------------------
CorreoID  LongInt        NOT NULL
- - - - - - - - - - - - - - - - - - (Campos Llave)
De        CHAR(80)       NOT NULL
Para      CHAR(80)       NOT NULL
Titulo    CHAR(255)      NULL
Fecha     DATETIME       NOT NULL

Ahora bien, esto no es lo único que usted necesita en cuanto a tablas. A lo mejor querrá crear un sistema de subfolders para que el usuario pueda asignar correos a distintos archivos, etcétera. Usted decidirá como desea guardar e implementar el correo.

Porqué No Tablas

Sería muy sencillo utilizar tablas para guardar nuestros correos electrónicos, pero si usted se preocupa por compatibilidad e interoperatividad, tal vez le convenga más utilizar otros métodos. La decisión por supuesto es suya, pero mi responsabilidad es al menos dejarle saber las opciones.

El campo de Unix (que son los que inventaron esta clase de correo electrónico) tiene varios estándares para guardar correo, de los cuales el más sencillo de implementar es un archivo de texto llamado "mbox". MBox es un formato muy simple basado en un simple archivo de texto, que usted puede abrir incluso con el block de notas para ver cómo está escrito.

Muchos programas pueden leer y escribir correos a formato mbox, y usted puede implementar mbox en solo unos cuantos días (en mi projecto minimailer tengo un lector de mbox limitado que podría ser fácilmente modificado a escritura que me tomó unos cuatro días implementar). En Windows, Netscape Navigator y Communicator utilizan el formato Mbox para guardar correo.

¿Porqué utilizar un estándar? Hoy día casi toda la gente utiliza un programa de correo electrónico. Si usted quiere que la gente use su correo, como mínimo deberá usted poder leer el correo desde otro archivo. La utilización de un estándar basado en texto garantiza que usted podrá leer e interoperar con otros sistemas de correo electrónico. Si usted le da a sus usuarios una opción en cuanto a sus archivos (en este caso la opción de dejar de usar su programa, pero sin perder sus e-mails, o de intercambiar entre Netscape y su programa), sus usuarios tendrán una mejor experiencia con su programa, y aún cuando decidan que no lo van a usar, le garantizo que al menos estas personas bajarán el próximo programa que usted haga, porque hizo las cosas "de la manera correcta".

Por ejemplo, yo utilizo Linux para mi e-mail. El mes pasado decidí dejar de usar el lector de correo de Netscape Communicator Linux, y comenzar a ver diferentes opciones. He utilizado Mutt, Elm, KDE Mail, GnoMail, y todos tienen lo mismo en común - Pueden leer el e-mail que tengo archivado desde hace ya algunos años, sin necesidad de convertirlo. Obviamente, con varios años de correo archivado, no quiero tener que convertir de un formato a otro cada vez que voy a cambiar de lector de correo (que puede ser muchas veces si quiero probar varios antes de llegar a una decisión). De hecho, esa es la razón por la cual escribi mi pequeño "minimailer", que es únicamente un parser de mboxes.

Leyendo Correo del Servidor

Voy a suponer que tenemos nuestro archivador de correo escrito (en este ejemplo utilizaremos una version modificada de minimailer) y entraremos en materia: ¿Cómo hacemos para leer correo de nuestro servidor?

Esta vez, como estamos comenzando con una versión un tanto avanzada de un programa existente, he decidido añadir un módulo de datos, que será el que haga las tareas de conexión al servidor. Ésto mantendrá los cambios a la forma principal al mínimo y es para que le sea lo más simple posible seguir el código en el ejemplo. La mayor parte del código importante se encuentra en dmMiniMailer.pas.

Comenzando con una versión modificada (y traducida al español) del projecto MiniMailer, añadiremos un componente POP3 a la forma principal. Necesitaremos especificar un nombre del servidor de correo al que nos queremos conectar, y un nombre de usuario/password. En este caso, tenemos en la forma principal un menú llamado "Leer Mensajes". Así que modificamos el OnClick para que muestre una forma de diálogo y después ejecute un comando para obtener mensajes dentro de nuestro módulo de datos El "CorreoPop" es el componente NMPop3:


procedure TfrmMailer.LeerMensajes1Click(Sender: TObject);
begin

  // Pide la clave de acceso y se conecta al servidor.
  with TPasswordDlg.Create(Self) do try
    ShowModal;
    if ModalResult = mrOk then begin
      dmMailer.CorreoPop.Host := edtServidor.Text;
      dmMailer.CorreoPop.UserID := edtUsuario.Text;
      dmMailer.CorreoPop.Password := edtClave.Text;
      dmMailer.ObtenMensajes(TreeView1);
    end;
  finally
    Free;
  end;

end;

Este código preparará el componente con las partes más importantes - servidor de correo, nombre de usuario y clave de acceso. Ahora necesitaremos implementar ObtenMensajes dentro de nuestro módulo de datos:


procedure TDmMailer.ObtenMensajes( Sender : TTreeView );
var
  i : Integer;
  dlg : TDlgNuevosMensajes;
begin

  CorreoPop.Connect;
  dlg := TdlgNuevosMensajes.Create(Self);
  try
    if CorreoPop.MailCount > 1 then begin
      for i := 0 to CorreoPop.MailCount do begin
         CorreoPop.GetSummary(i);
         // Muestra los sumarios de los mensajes en el servidor
         dlg.sgNuevosMensajes.Cols[0].Add(CorreoPop.Summary.From);
         dlg.sgNuevosMensajes.Cols[1].Add(CorreoPop.Summary.Subject);
         dlg.sgNuevosMensajes.Cols[2].Add(IntToStr(CorreoPop.Summary.Bytes div 1024));
      end;
    end else Raise Exception.Create('No hay mensajes en el servidor');
    dlg.ShowModal;
    if dlg.ModalResult = mrOk then LeeMensajes(Sender);
  finally
    dlg.Free;
    CorreoPop.Disconnect;
  end;

end;

Básicamente, el componente NMPop3 (llamado "CorreoPop" en este ejemplo) puede pedir al servidor los sumarios de todos los mensajes independientemente de los mensajes en sí. Así que utilizamos este código para mostrar los sumarios en un diálogo simple (con un StringGrid, 3 columnas y botones Ok/Cancelar). Si el usuario presionó "OK", pasamos el Treeview que recibimos en "Sender" a "LeeMensajes". Veamos la implementación de "LeeMensajes":


procedure TdmMailer.LeeMensajes( Sender : TTreeView );
var
  i : Integer;
  ElNodo : TTreeNode;
  MailBox : TMBox;
  tmpMensaje : TMensajeCorreo;
begin

  // Aquí llenamos el árbol con los MBoxes de un directorio.
  ElNodo := Sender.Items.Add( Nil, 'Nuevos Mensajes');
  ElNodo.ImageIndex := Ord(imbMbox);
  ElNodo.SelectedIndex := Ord(imbMbox);
  ElNodo.Data := MailBox;
  MailBox := TMBox.Create;

  for i := 0 to CorreoPop.MailCount -1 do begin
     tmpMensaje := TMensajeCorreo.Create;
     CorreoPop.GetMailMessage(i);
     tmpMensaje.MailBody.Assign(CorreoPop.MailMessage.Body);
     tmpMensaje.Attributes.Assign(CorreoPop.MailMessage.Head);
     tmpMensaje.MailSubject := CorreoPop.MailMessage.Subject;
     tmpMensaje.MailFrom := CorreoPop.MailMessage.From;
     tmpMensaje.MailMsgId := CorreoPop.MailMessage.MessageId;
     MailBox.MessageList.AddMessage(tmpMensaje);
  end;

end;

LeeMensajes es donde leemos los mensajes. Aquí Añadimos un nuevo nodo al árbol que recibimos en "Sender", y le asignamos el ícono de "imbMailbox". Después creamos un MBox (que la estructura que utilizo para leer los archivos tipo mbox de disco, definida en "mbox.pas"), y añado tantos TMensajecorreo(s) como requiera, dependiendo de cuantos mensajes existan en CorreoPop.MailCount. Asigno los valores necesarios (Body y Head son TStringLists) y al final añado el puntero de mi nuevo TMensajeCorreo al TMBox. Esto definirá los punteros de la misma manera que se definirían si hubiera yo leido un MBox de disco, así que no habrá problema para desplegarlo con el código que ya escribí.

La versión modificada de minimailer (que he llamado "correito") tiene muchos comentarios en español para que usted sepa lo que está pasando en cuanto al despliegue de los correos electrónicos, etcétera. Explore el programa y verá qué fácil es!

TODO: Debug the program / Screenshots while connected to a POP server, and post source.

Enviando Correo via SMTP

Enviar correo es más sencillo que leerlo. En la modificación que haremos para que el minimailer envíe correo, simplemente añadiremos una opción al menú para que muestre una simple forma de envío de correo, muy parecida a varias que seguramente ha visto. Y después utilizaremos el componente NMSMTP para que nuestro programa pueda enviar correo electrónico.

Copiar el Código fuente (17K, formato Zip) del MiniMailer (Inicial)