Indy POP3 Client / Server (email) - John COLIBRI. |
- résumé : lecteur de mails : un exemple simple d'un serveur et d'un lecteur d'email Indy : les instructions de démarrage / connexion, les commandes POP3, l'affichage de la lecture de mails
- mots clé : POP3 - lecture de mail - serveur POP3 - lecteur POP3 - affichage Indy Vcl - tidSync - tIdPop3 - tIdPop3Server - LIST RETR DELE
- logiciel utilisé : Windows XP personnel, Delphi 2006, Indy 9
- matériel utilisé : Pentium 2.800 Mhz, 512 Meg de mémoire, 250 Giga disque dur
- champ d'application : Delphi 6, Delphi 7, Delphi 2006, Delphi 2007, Delphi 2009, Delphi 2010, Delphi XE,
- niveau : développeur Delphi
- plan :
1 - Lecture et Ecriture de Mails Cet article présente des projets pour lire des email en utilisant Indy. Internet a décomposé la gestion des emails en deux parties
- la partie SMTP (Simple Mail Transfer Protocol) qui gère
- l'envoi d'un courriel depuis votre PC (le Client SMTP)
- la réception de ces données sur un serveur distant (le Serveur SMTP).
Chaque fois que vous envoyez un mail, vous utilisez un client SMTP. Ce mail est envoyé vers une poste serveur, où un Serveur SMTP passe son temps à recevoir vos mail (et ceux des autres utilisateurs de ce serveur) et à les
archiver. Cet archivage n'est pas régi par des règles Internet, et chaque serveur (Wanadoo / Orange, Free, Club Internet, mon propre serveur qui gère les mails que vous nous envoyez sur jcolibri@jcolibri.com) est libre de stocker les mails à sa façon.
Le poste serveur sert donc essentiellement à stocker les mails envoyés. La partie SMTP s'occupe de les recevoir et de les archiver, en attente d'une éventuelle lecture. Ceci peut être représenté ainsi :
- la partie POP3 (Post Office Protocol 3) qui gère
- la lecture d'un mail depuis votre PC (le Client POP3)
- la recherche du mail sur le disque du serveur de mail, et son envoi à celui qui l'a demandé (le Client POP3)
La partie POP3 sert donc simplement à rechercher sur le serveur les mails
qui quelqu'un a stocké sur le poste serveur à notre attention et à nous les faire parvenir via Internet Ce que nous pouvons illustrer comme suit:
Ces envois et lectures de mail sont en général effectués depuis des outils tels que Outlook Express, mais vous pouvez, avec avantage, créer vos propres clients SMTP et POP3:
- votre client SMTP pourra, par exemple, intégrer une mécanique de fond de page ou tout autre traitement sur mesure (archivage, cryptage etc) avant l'envoi vers le serveur SMTP
- votre client POP2 pourra mettre en place des filtrages de spam très
perfomant, des organisation de stockages plus élaborées qu'Outlook, une gestion plus facile de la lecture des mails et pièces jointes
Il est plus rare de mettre en place des serveurs SMTP ou POP3, car cela impose
l'utilisation et la mise en place d'un IP connu de vos interlocuteurs (Ip dynamique ou Ip fixe), et n'a de raison d'être que si vous ne souhaitez pas utiliser les serveurs commerciaux usuels. Pour info, c'est ce que nous avons
fait pour pouvoir avoir un mail "jcolibri@jcolibri.com", plutôt que "jcolibri@free.fr"
Bien qu'il soit rare d'avoir à mettre en place à la vois un client et un serveur SMTP, ou un client et un serveur POP3, nous allons dans cet article au
contraire présenter les deux interlocuteurs. Ceci permet - de mettre au point le fonctionnement des deux côtés. Même si vous ne souhaitez écrire que le Client Pop3, par exemple, l'utilisation de votre
propre Serveur Pop3 pourra être utilisé pour la mise au point et le réglage de votre Client
- de mieux comprendre le protocole POP3 ou SMTP
Nous avons choisi pour cet article d'examiner la partie POP3.
2 - Le Protocole POP3 2.1 - Les Commandes Le protocole prévoit un certain nombre de commandes: - le client envoie le nom de la commande suivi de paramètres éventuels
- le serveur renvoie sa réponse. La réponse débute par "+OK" ou "-ERR", suivi de données éventuelles
Voici les commandes du protocole POP3:
2.2 - USER <username>
| le Client POP3 envoie l'identificateur pour authentification <username> est l'adresse e-mail du client |
| le Serveur POP3 répond. En général, le serveur renvoie toujours "+OK", même si le nom est incorrect (pour éviter les avalanches de spam) | Exemple
USER jcolibri@jcolibri.com |
2.3 - PASS <password> |
le Client POP3 envoie son mot de passe. Cette commande est envoyée juste après USER | Exemple:
2.4 - STAT
| le Client POP3 envoie STAT | |
le Serveur POP3 retourne le nombre de messages et le nombre total d'octets | Exemple:
2.5 - LIST
| le Client POP3 envoie LIST | | le Serveur POP3 retourne
- le nombre de messages et le nombre d'octets
- la liste des numéros de chaque message et leur taille
- un point sur une ligne
| Exemple:
LIST
+OK 4 1486665 1 976 2 977 3 975 4 1483737 . | |
Il est possible de demander les statistiques d'un numéro de message particulier, avec une réponse négative s'il n'existe pas
2.6 - RETR <messages number>
| le Client POP3 envoie RETR et un numéro de message | | le Serveur POP3
- retourne
- +OK
- le message complet (en-tête, ligne blanche, contenu) au format MIME
- un point sur une ligne
| Exemple:
RETR 3
+OK 235 octets From: "felix" <huckleberry@free.fr> To: <jcolibri@jcolibri.com> Subject: test
Date: Mon, 18 Jan 2010 19:32:02 +0100 Ceci est un test . | |
2.7 - DELE <message number> | le Client POP3 envoie DELE et un numéro de message |
| le Serveur POP3 - marque le message comme devant être effacé après QUIT
- retourne +OK (plus un texte éventuel) ou une erreur
| Exemple:
2.8 - RSET Cette commande est optionnelle.
| le Client POP3 envoie RSET | | le Serveur POP3 retourne +OK (plus un texte éventuel) et réinitialise la session :
- les messages marqués pour effacement ne sont plus marqués
| Exemple:
2.9 - TOP <message number> [<number of lines>] Cette commande est optionnelle |
le Client POP3 envoie TOP, le numéro de message, et optionnellement le nombre de lignes à retourner | | le Serveur POP3 retourne
- le status
- l'en-tête du message
- si un nombre de lignes est spécifié, seul ces lignes du contenu sont retournées
| Cette commande permet d'avoir une idée du contenu du message, sans le transférer en entier
Exemple: TOP 2 3
+OK [headers of message 2] -------=_Part_7245_2349853.2340985098374 Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit . | |
2.10 - UIDL [<message number>] Cette commande est optionnelle
| le Client POP3 envoie UIDL et, optionnellement un numéro de message | |
le Serveur POP3 retourne - le status
- la liste des numéros de message et leur identifiant
| Exemple:
UIDL +OK 1 4239F2D2DED94A21A801F4A14796B0C0
2 16CAE91D6A9B488E89587A225EAC8F3E 3 A7FE63D715B849489B3EE60903AD9E94 4 83CA8D76A6EB4229B6D6F501BABCFD13 . | |
2.11 - QUIT | le Client POP3 envoie QUIT |
| le Serveur POP3 répond et efface les messages marqués pour effacement | Exemple:
2.12 - Autres commandes POP3 contient quelques autres commandes non essentielles - NOOP : opération blanche
- APOP, qui est une autre commande d'authentification
2.13 - Une session Voici un exemple de session
le serveur est lancé et attend les clients sur le port 110 |
notre client se connecte +OK POP3 server ready <1896.697170952@dbc.mtview.ca.us> |
USER jcolibri@jcolibri.com PASS xyz
LIST
+OK 4 1486665 1 976 2 977 3 235
4 1483737 . | RETR 3
+OK 235 octets From: "felix" <huckleberry@free.fr> To: <jcolibri@jcolibri.com> Subject: test
Date: Mon, 18 Jan 2010 19:32:02 +0100 Ceci est un test . | DELE 3
QUIT
+OK GOODBYE maildrop has 3 messages (320 octets) | le client se déconnecte
le serveur gère les autres clients | |
3 - Le serveur POP3 Indy et le client POP3 Indy 3.1 - Serveur POP3 Indy : tIdPop3Server Voici le diagramme de classe UML de tidPop3Server
Et: - tIdPop3Server n'a qu'une propriété, DefaultPort, qui est en général 110
- tidPop3Server a un événement par commande principale. Chacune de ces
commandes a un paramètre tIdCommand, qui donne accès à tIdPeerThread qui donne accès à tIdConnection.
- un descendant de tIdPeerThread contient les données pour gérer l'authentification (CheckUser)
- tIdPop3Server génère des événements après chaque réception de commande Pop3 du client, et c'est dans ces événements que nous sommes censés gérer les données qui sont stockés sur le serveur: nombre de mails, taille, liste,
renvoi, effacement etc
Donc : - lorsque le Serveur reçoit une connexion, il crée un tidPop3ServerThread qui va dialoguer avec ce client pour gérer la lecture de ses mails. Cet objet
- a accès au socket pour lire et écrire (Connection)
- contient les champs UserName et PassWord
- le Serveur reçoit les commandes USER et PASS et garnit les champls
UserName et PassWord de tIdPop3ServerThread.
Il déclenche l'événement CheckUser que nous pouvons utiliser pour effectuer nos filtrages.
- si nous avons accepté le message, nous utilisons tIdPop3Server.OnRETR pour chercher le texte du message et l'envoyer, en utilisant, par exemple, une succession de WriteLn
3.2 - Fonctionnement client tIdPop3 Voici le diagramme de classe UML du client tIdPop2 :
tIdPop3Client permet simplement d'envoyer des commandes Pop3 (avec les paramètres spécifiés), et de recevoir les réponses. - pour nous connecter, nous initialisons Host, UserName, PassWord
et appellons Connect - nous envoyons les différentes commandes Pop3 en utilisant IdPop3.SendCmd, et les réponses éventuelles sont récupérées sous forme de tStrings comme paramètre de tIdPop3.Capture
- pour RETR, nous utilisons la procédure tIdPop3.Retrieve, qui retourne le mail dans un tIdMessage que nous fournissons comme paramètre
3.3 - Démonstration tIdPop3, tIdPop3Server
Pour notre démonstration - côté Serveur
- posez un tIdPop3Server
- posez un bouton "start" et un bouton "stop" qui basculent Active
- créez les événements CheckUser, OnLIST, OnRETR, OnDELE (code ci-dessous)
- côté Client
- posez un client tIdPop3, initialisez Host, UserName, PassWord
- posez un tIdAntiFreeze et un tIdMessage
- posez des boutons pour connecter et déconnecter
- posez un bouton "list" et écrivez le code pour envoyer la commande et récupérer la liste des messages (code ci-dessous)
- posez un bouton "retrieve", demandez et récupérez un message
- idem pour "delete"
3.4 - Scénario Pop3 Indy Un scénario Pop3 serait par exemple le suivant
| IdPop3Server1 est démarré (IdPOP3Server1.Active basculé à True) | |
IdPop3Client1 initialise - Host (127.0.0.1)
- UserName (my_user)
- PassWord (my_pass)
et appelle Connect |
| IdPop3Server1 déclenche CheckUser, et, après vérification, nous acceptons ou refusons cet utilisateur :
Procedure TForm1.IdPOP3Server1CheckUser(AThread: TIdPeerThread;
LThread: TIdPOP3ServerThread); Begin
If (LThread.Username ... ) And (LThread.Password ...)
Then LThread.State:= Trans;
End; // IdPOP3Server1CheckUser | | |
IdPop3Client1 demande la liste des mails non lus:
Procedure TForm1.list_Click(Sender: TObject);
Begin With IdPop31 Do
If SendCmd('LIST')= wsOk
Then Capture(ListBox1.Items);
End; // list_Click | | |
IdPop3Server1 déclenche OnLIST, où nous retournons la liste et les numéros des messages (ici abrégé)
Procedure TForm1.IdPOP3Server1LIST(ASender: TIdCommand; AMessageNum: Integer);
Begin
ASender.Thread.Connection.WriteLn('+OK '+ IntToStr(Count)+ ' 120');
For l_file_index:= 0 To Count- 1 Do
ASender.Thread.Connection.WriteLn(IntToStr(1+ l_file_index)+ ' 40');
End; | | |
IdPop3Client1 demande la réception d'un message ayant un numéro donné dans IdMessage1 :
Procedure TForm1.retrieve_Click(Sender: TObject);
Begin IdPop31.Retrieve(3, IdMessage1); |
et attend la réponse du serveur | | IdPop3Server1 déclenche OnRETR, et utilise le numéro de message pour le
chercher et le retourner à l'expéditeur (ici simplifié)
Procedure TForm1.IdPOP3Server1RETR(ASender: TIdCommand; AMessageNum: Integer);
Var l_c_message: tStringList; Begin
l_c_message:= tStringList.Create;
l_c_message.LoadFromFile(ExtractFilePath(Application.ExeName)
+ '\_data_pop3\'+ IntToStr(aMessageNum)+ '.txt');
With ASender.Thread.Connection Do
Begin
WriteLn('+OK '+ IntToStr(Length(l_c_message.Text))+ ' octets');
WriteLn(l_c_message.Text);
WriteLn('.'); End;
l_c_message.Free; End; // IdPOP3Server1RETR |
| | IdPop3Client1 reprend le contrôle après l'appel à RETR, et utilise le message qui a été déposé et analysé par IdMessage1 :
With IdMessage1 Do
Begin
display('From '+ From.Text);
With Recipients.Items[0] Do
display('To '+ Name+ ' '+ Address);
display('Subject '+ Subject);
display('Content '+ Body.Text);
End; End; // retrieve_Click |
Puis, IdPop3Client1 demande l'effacement du message
IdPop3.SendCmd('DELE 3'); | | |
IdPop3Server1 déclenche OnDELE, et marque le message comme étant "à supprimer" | | IdPop3Client1 envoie QUIT et se déconnecte |
| IdPop3Server1 gère OnQUIT où les messages sont effacés et libère le socket utilisé pour gérer ce client-là |
| IdPop3Server1 se termine (Active devient False) |
3.5 - Le résultat
Lancez le Serveur ("start"), et lorsque vous le souhaiterez, fermez le ("stop") :
Pour le Client, cliquez "connect", "list", sélectionnez l'un des messages,
cliquez "retrieve", "delete" et "disconnect"
Notez que - les sources que vous pouvez
télécharger contiennent d'autres événements que nous n'avons pas détaillés ici
- notre Client se connecte en utilisant 127.0.0.1. Toutefois, sur XP Pro, il
vaut mieux utiliser l'IP du Serveur (par exemple 192.168.0.1)
4 - Améliorations 4.1 - L'exemple de Client et Serveur Au niveau du fonctionnement de notre exemple
- nous aurions pu implémenter des dialogues plus complexes (lecture de HEAD ou TOP, possibilité d'annuler les effacements par RESET etc)
- côté Serveur, nous avons omis
- la gestion des message. Celle-ci doit naturellement synchronisée avec la gestion mise en place par le Serveur SMPT
- la gestion des exceptions et fermeture des Clients
- côté Client, manquent, entre autres,
- la gestion utilisateur du lecteur: (possibilité de lire depuis plusieurs boîtes aux lettres, ...)
- la présentation de la liste des messages
- la gestion des pièces jointes
- le filtrage des spams
- l'archivage des mails reçus ou rejetés
- la possibilité de répondre (ajout d'un Client SMTP)
- nous n'avons pas du tout géré les exceptions, ni la clôture intempestive côté Client ou Serveur
4.2 - Perspectives Indy Notez que: - Indy 8 ne comporte pas de serveur POP3. Téléchargez la version 9 ou 10 de Indy
- comme indiqué au début de l'article, nous avons ici utilisé la version Indy 9
- le fait qu'il faille utiliser tIdPop3.Retrieve, tIdPop3.Capture, tIdPOP3ServerThread.State n'est pas d'une évidence aveuglante. Indy
effectue énormément de travail d'analyse et de traitement pour nous, mais les propriétés et procédure à utiliser ne sont pas toujours bien documentés
- le Serveur utilise tIdSync pour permettre l'affichage dans la VCL. Ces
techniques sont IMPERATIVES pour éviter les problèmes de threads. Sans ce type de précautions, nous encourrons des bugs des plus sournois: exceptions, fermeture, ou pire, non fonctionnement sans aucun diagnostic. Ceci souligne
bien que sans une bonne connaissance des threads, l'utilisation de Indy est assez délicate
- le Client comporte le traditionnel tIdAntiFreeze, qui évite le gel du
client en cas de lecture bloquante depuis le Client. Ici aussi, la connaissance du fonctionnement en mode sockets bloquant est utile
4.3 - Formations Tcp Ip
Pour les personnes qui souhaiteraient approfondir les communications Tcp/Ip, et surtout maîtriser les détails techniques de la librairie Indy, en particulier pour construire des protocoles sur mesure, nous organisons régulièrement des
Formation Tcp/Ip sockets Delphi de deux jours.
4.4 - Diagrammes de Classe UML
Les diagrammes de classe UML permette de mieux comprendre l'articulation entre les différents composants Indy. Indy est une librairie à granularité fine. Pour comprendre quelle propriété de quel composant utiliser, il faut de toutes les
façons vous construire une image mentale des relations entre ces composants. Et là, miracle, les diagrammes de classe UML vous présente en quelques figures tout ce qu'il y a savoir. Outre les articles comme celui-ci, pour nos
formations, pour nos propres explorations de librairies, nous employons énormément UML. Pour ces librairies complexes, hors UML, pas de salut !
5 - Télécharger le code source Delphi Vous pouvez télécharger: Ce .ZIP qui comprend: - le .DPR, la forme principale, les formes annexes eventuelles
- les fichiers de paramètres (le schéma et le batch de création)
- dans chaque .ZIP, toutes les librairies nécessaires à chaque projet (chaque .ZIP est autonome)
Ces .ZIP, pour les projets en Delphi 6, contiennent des chemins RELATIFS. Par conséquent:
- créez un répertoire n'importe où sur votre machine
- placez le .ZIP dans ce répertoire
- dézippez et les sous-répertoires nécessaires seront créés
- compilez et exécutez
Ces .ZIP ne modifient pas votre PC (pas de changement de la Base de Registre, de DLL ou autre). Pour supprimer le projet, effacez le répertoire. La notation utilisée est la notation alsacienne qui consiste à préfixer les
identificateurs par la zone de compilation: K_onstant, T_ype, G_lobal, L_ocal, P_arametre, F_unction, C_lasse. Elle est présentée plus en
détail dans l'article La Notation Alsacienne
Comme d'habitude:
- nous vous remercions de nous signaler toute erreur, inexactitude ou problème de téléchargement en envoyant un e-mail à jcolibri@jcolibri.com. Les corrections
qui en résulteront pourront aider les prochains lecteurs
- tous vos commentaires, remarques, questions, critiques, suggestion d'article, ou mentions d'autres sources sur le même sujet seront de même
les bienvenus à jcolibri@jcolibri.com.
- plus simplement, vous pouvez taper (anonymement ou en fournissant votre e-mail pour une réponse) vos commentaires ci-dessus et nous les envoyer en
cliquant "envoyer" :
- et si vous avez apprécié cet article, faites connaître notre site,
ajoutez un lien dans vos listes de liens ou citez-nous dans vos blogs ou réponses sur les messageries. C'est très simple: plus nous aurons de visiteurs et de références Google, plus nous écrirons d'articles.
6 - Références
7 - L'auteur John COLIBRI est passionné par le développement Delphi et les applications de Bases de Données. Il a écrit de nombreux livres
et articles, et partage son temps entre le développement de projets (nouveaux projets, maintenance, audit, migration BDE, migration Xe_n,
refactoring) pour ses clients, le conseil (composants, architecture, test) et la
formation. Son site contient des articles
avec code source, ainsi que le programme et le calendrier des stages de formation Delphi, base de données, programmation objet, Services Web, Tcp/Ip et
UML qu'il anime personellement tous les mois, à Paris, en province ou sur site client. |