Tutoriel pour apprendre à envoyer et recevoir des emails avec Javamail

Denis Thomas

Ce tutoriel aura pour but de vous apprendre à manipuler l'API Javamail, dans ses fonctions essentielles, à savoir envoyer et recevoir des mails, depuis de simples messages textes jusqu'à de complexes messages HTML avec pièces jointes.

Commentez

- Titre : Tutoriel pour apprendre à envoyer et recevoir des emails avec Javamail

- Auteur : Denis Thomas

- Parution : 15 septembre 2013

- Public visé :

- Licence : Le contenu de cet article est rédigé par Denis Thomas et est mis à disposition selon les termes de la Licence Creative Commons Attribution 3.0 non transposé. Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright ® 2013 Developpez.com.

Sommaire

- I. Téléchargement

- II. Messages texte simples

- II-A. Envoi

- II-B. Réception

- III. Messages avec pièces jointes

- III-A. Composition

- III-B. Lecture du message

- IV. Messages HTML

- V. Conclusion

- VI. Remerciements

- VII. Liens

I - Téléchargement

Avant de commencer, nous devons bien entendu installer Javamail. Votre moteur de recherche préféré vous dira où le télécharger, il n'y a pas besoin de chercher bien longtemps. Mais le plus simple est d'utiliser Maven :

<dependencies>
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.3</version>
</dependency>
</dependencies>

Cela étant fait fait, nous pouvons nous attaquer à l'envoi et la réception de nos messages.

II - Messages texte simples

II-A - Envoi

L'envoi du message se fait en trois étapes :

- création d'une Session ;

- création d'un objet Message, auquel nous ajoutons les destinataires ;

- envoi du message, grâce à un objet Transport.

Voyons ces étapes une à une :

1. public static void sendMessage(String subject, String text, String destinataire, String copyDest) {

2. // 1 -> Création de la session

3. Properties properties = new Properties();

4. properties.setProperty("mail.transport.protocol", "smtp");

5. properties.setProperty("mail.smtp.host", SMTP_HOST1);

6. properties.setProperty("mail.smtp.user", LOGIN_SMTP1);

7. properties.setProperty("mail.from", IMAP_ACCOUNT1);

8. Session session = Session.getInstance(properties);

10. ...

12. }

Pour construire une Session, nous avons besoin de quelques propriétés, comme le protocole (SMTP dans notre cas), le serveur, ainsi que des informations d'identification, mais sans le mot de passe. Ce dernier nous sera demandé lors de la connexion par l'objet Transport. Pour connaître l'ensemble des propriétés, je vous renvoie à la documentation officielle de Javamail.

Maintenant que nous avons notre session, créons notre message :

1. public static void sendMessage(String subject, String text, String destinataire, String copyDest) {

2. ...

4. // 2 -> Création du message

5. MimeMessage message = new MimeMessage(session);

6. try {

7. message.setText(text);

8. message.setSubject(subject);

9. message.addRecipients(Message.RecipientType.TO, destinataire);

10. message.addRecipients(Message.RecipientType.CC, copyDest);

11. } catch (MessagingException e) {

12. e.printStackTrace();

13. }

15. ...

16. }

Rien de compliqué : on instancie notre message en lui passant en paramètre la session, puis on ajoute le texte, le sujet et les destinataires. Reste à l'envoyer :

1. public static void sendMessage(String subject, String text, String destinataire, String copyDest) {

2. ...

4. // 3 -> Envoi du message

5. Transport transport;

6. try {

7. transport = session.getTransport("smtp");

8. transport.connect(LOGIN_SMTP1, PASSWORD_SMTP1);

9. transport.sendMessage(message, new Address[] { new InternetAddress(destinataire),

10. new InternetAddress(copyDest) });

11. } catch (MessagingException e) {

12. e.printStackTrace();

13. } finally {

14. try {

15. if (transport != null) {

16. transport.close();

17. }

18. } catch (MessagingException e) {

19. e.printStackTrace();

20. }

21. }

22. }

C'est tout aussi simple : notre session nous donne un objet Transport, qui nous permet de nous connecter au serveur SMTP et d'envoyer le message à nos destinataires. Attention, pour créer l'objet Transport à partir de la session, pensez à mettre le protocole en minuscules ! Et bien entendu, on ferme notre connexion.

Rien de bien compliqué, mais nous avons cependant affaire à quelques subtilités. En premier lieu, il est possible, mais pas toujours, d'envoyer des messages sans indiquer les propriétés. Tout dépend du serveur SMTP et de sa configuration. J'ai réussi avec l'un, pas avec l'autre (smtp.laposte.net). Le mieux est donc de les mettre systématiquement. Ensuite, la propriété mail.host doit impérativement correspondre au nom du serveur SMTP. En général, c'est celui utilisé pour l'envoi, mais pas toujours. Si on se trompe, tout ce qu'on obtient est une exception :

com.sun.mail.smtp.SMTPAddressFailedException: 554 5.7.1 <yy@y.com>: Relay access denied

Votre message est simplement considéré comme du SPAM.

Une autre exception possible est celle-ci :

com.sun.mail.smtp.SMTPSendFailedException: 530 5.7.0 Must issue a STARTTLS command first. ed6sm6117146wib.9 - gsmtp

Elle est due au fait que votre serveur SMTP utilise une connexion sécurisée. C'est notamment le cas de smtp.gmail.com. Le problème se résout très simplement en ajoutant cette propriété :

properties.setProperty("mail.smtp.starttls.enable", "true");

Quant aux destinataires du message, notre message n'a nul besoin de les connaître pour être acheminé. Seul notre objet Transport en a besoin. Enlevez les deux lignes message.addRecipients(), et essayez, vous verrez que votre message arrive à bon port malgré tout. Seulement, le destinataire ne saura jamais à qui d'autre que lui il était destiné.

Faites quelques essais avec votre client de mail favori, et voyez les résultats.

Maintenant que nous savons envoyer un message, il ne nous reste plus qu'à le recevoir.

II-B - Réception

Pour ces essais, fermez votre client de courriel utilisé jusqu'ici, et faites attention à ce que vous faites : nous allons lire des mails, mais pas seulement ceux de notre exemple. Le code fourni ne supprime pas les messages du serveur, mais si vous faites d'autres essais, pensez-y !

Comme pour l'envoi, la réception des messages a besoin d'un objet Session :

1. private static void receiveMessage() {

2. // 1 -> La session

3. Properties properties = new Properties();

4. properties.setProperty("mail.store.protocol", "pop3");

5. properties.setProperty("mail.pop3.host", POP_SERVER3);

6. properties.setProperty("mail.pop3.user", POP_ACCOUNT3);

7. Session session = Session.getInstance(properties);

8. ...

9. }

C'est pareil que pour l'envoi, avec de nouvelles propriétés. La session que nous obtenons nous permet d'obtenir un objet Store, grâce auquel nous accéderons aux différents Folder contenant nos messages :

1. private static void receiveMessage() {

2. ...

4. // 2 -> Les dossiers

5. Store store = null;

6. Folder defaultFolder = null;

7. Folder inbox = null;

8. try {

9. store = session.getStore(new URLName("pop3://" + POP_SERVER3));

10. store.connect(POP_ACCOUNT3, POP_PASSWORD3);

11. defaultFolder = store.getDefaultFolder();

12. System.out.println("defaultFolder : " + defaultFolder.getName());

14. for (Folder folder : defaultFolder.list()) {

15. System.out.println(folder.getName());

16. }

17. inbox = defaultFolder.getFolder("INBOX");

18. printMessages(inbox);

19. } catch (Exception e) {

20. e.printStackTrace();

21. } finally { // Ne pas oublier de fermer tout ça !

22. close(inbox);

23. close(defaultFolder);

24. try {

25. if (store != null && store.isConnected()) {

26. store.close();

27. }

28. } catch (MessagingException e) {

29. e.printStackTrace();

30. }

31. }

32. }

Notez à ligne 9 la manière de récupérer un Store depuis une session par une URL. Pour une boite IMAP, on remplace pop3 par imap ou imaps selon les cas.

Un Store peut contenir plusieurs Folder. Nous partons donc du Folder racine (defaultFolder), puis nous imprimons ses enfants, qui pourraient à leur tour en contenir d'autres comme toute arborescence qui se respecte. Ici, celui qui nous intéresse est INBOX, la boite de réception tout simplement. Une fois que nous avons notre Folder, récupérer les messages et les imprimer est un jeu d'enfant :

1. private static void printMessages(Folder folder) {

2. try {

3. folder.open(Folder.READ_ONLY);

4. int count = folder.getMessageCount();

5. int unread = folder.getUnreadMessageCount();

6. System.out.println("Il y a " + count + " messages, dont " + unread + " non lus.");

7. for (int i = 1; i <= count; i++ ) {

9. Message message = folder.getMessage(i);

10. System.out.println("Message n° " + i);

11. System.out.println("Sujet : " + message.getSubject());

13. System.out.println("Expéditeur : ");

14. Address[] addresses = message.getFrom();

15. for (Address address : addresses) {

16. System.out.println("\t" + address);

17. }

19. System.out.println("Destinataires : ");

20. addresses = message.getRecipients(RecipientType.TO);

21. if (addresses != null) {

22. for (Address address : addresses) {

23. System.out.println("\tTo : " + address);

24. }

25. }

26. addresses = message.getRecipients(RecipientType.CC);

27. if (addresses != null) {

28. for (Address address : addresses) {

29. System.out.println("\tCopie : " + address);

30. }

31. }

33. System.out.println("Content : ");

34. for (String line : inputStreamToStrings(message.getInputStream())) {

35. System.out.println(line);

36. }

37. }

39. } catch (Exception e) {

40. e.printStackTrace();

41. }

42. }

Nous les comptons, et nous affichons leurs attributs : expéditeur, destinataires, ... Le contenu lui est un peu plus embêtant, puisque nous le récupérons sous forme d'InputStream. Sa transformation en String n'est cependant pas très complexe, surtout si vous avez lu l'excellent article d'Olivier Croisier sur java.io.

Remarquons que les messages sont numérotés à partir de 1, et non à partir de 0 comme on en a l'habitude en informatique.

Il ne nous reste plus qu'à fermer nos Folder, en prenant bien soin de lui dire de ne pas effacer les messages :

1. private static void close(Folder folder) {

2. if (folder != null && folder.isOpen()) {

3. try {

4. folder.close(false); // false -> On n'efface pas les messages marqués DELETED

5. } catch (Exception e) {

6. e.printStackTrace();

7. }

8. }

9. }

Et maintenant que nous savons envoyer et recevoir des messages textes, voyons ce qu'il en est des pièces jointes.

III - Messages avec pièces jointes

Que notre message ne soit composé que de simple texte, ou contienne des pièces jointes, son envoi ne change pas, heureusement. Nous allons donc nous consacrer uniquement à sa composition et à sa lecture.

III-A - Composition

Un message avec pièces jointes est composé de plusieurs parties : le texte du message, plus les pièces jointes. Pour transporter tout cela, nous utilisons les types MIME. Le type du texte sera text/plain, quant à celui de nos pièces jointes, ça dépend. Le message contiendra donc un objet MimeMultiPart, auquel nous allons adjoindre un MimeBodyPart par partie du message.

Commençons par une belle image d'autruche :

Les étapes pour obtenir le MimeBodyPart de l'autruche sont les suivantes :

1. créer un objet File. C'est notre pièce jointe ;

2. grâce à lui, nous pouvons instancier une FileDataSource ;

3. cette FileDataSource nous permettra d'obtenir un DataHandler ;

4. nous créons ensuite notre MimeBodyPart, auquel nous ajouterons notre DataHandler ;

5. nous indiquons finalement à notre MimeBodyPart le nom du fichier. Nous verrons les conséquences de l'oubli de cette étape.

Voici le code, à répéter pour chaque pièce jointe :

1. File file = new File(PATH_TO_FILE + "/autruche.jpg");

2. FileDataSource datasource1 = new FileDataSource(file);

3. DataHandler handler1 = new DataHandler(datasource1);

4. MimeBodyPart autruche = new MimeBodyPart();

5. try {

6. autruche.setDataHandler(handler1);

7. autruche.setFileName(datasource1.getName());

8. } catch (MessagingException e) {

9. e.printStackTrace();

10. }

Ajouter un fichier mp3 se fait exactement de la même manière. Pour notre texte, c'est plus simple :

1. MimeBodyPart content = new MimeBodyPart();

2. try {

3. content.setContent("Texte du message", "text/plain");

4. } catch (MessagingException e) {

5. e.printStackTrace();

6. }

Maintenant, plaçons nos MimeBodyPart dans un MimeMultiPart :

1. MimeMultipart mimeMultipart = new MimeMultipart();

2. try {

3. mimeMultipart.addBodyPart(content);

4. mimeMultipart.addBodyPart(autruche);

5. mimeMultipart.addBodyPart(musique);

6. } catch (MessagingException e) {

7. e.printStackTrace();

8. }

Il ne nous reste plus qu'à créer notre message, y placer notre MimeMultiPart et à l'envoyer :

1. try {

2. message.setContent(mimeMultipart);

3. message.setSubject("sujet");

4. // envoi du message

5. ...

6. } catch (MessagingException e) {

7. e.printStackTrace();

8. }

Nous obtenons notre objet message exactement comme précédemment à l'aide d'une Session, et nous l'envoyons de la même manière. Voilà, notre message est parti. Regardons un peu ce qu'il advient des pièces jointes lors de sa réception si on oublie d'indiquer le nom du fichier.

Cela n'empêche nullement ceux-ci d'être transmis. Seulement, le destinataire du message ne saura jamais quel type de fichier il a reçu. Pour l'image, ça va encore, le message contient le type mime :

------=_Part_0_1167368365.1361654391542
Content-Type: image/jpeg
Content-Transfer-Encoding: base64

/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAAHgAA/+4ADkFkb2JlAGTAAAAAAf/b
AIQAEAsLCwwLEAwMEBcPDQ8XGxQQEBQbHxcXFxcXHx4XGhoaGhceHiMlJyUjHi8vMzMvL0BAQEBA
...

Pour notre musique, ça ne va pas du tout, le type mime est inconnu :

------=_Part_0_1167368365.1361654391542
Content-Type: application/octet-stream
Content-Transfer-Encoding: base64

SUQzAwAAAAVbdlRSQ0sAAAADAAAAMTFUQUxCAAAAEQAAAE1hZGFtZSBsYSBHaXJhZmVUQ09OAAAA
EQAAAENoaWxkcmVuknMgTXVzaWNBUElDAAFdGgAAAGltYWdlL3BuZwAAAIlQTkcNChoKAAAADUlI
...

Le fichier est devenu un simple flux d'octets. En passant, remarquons que nos pièces jointes sont encodées en Base64, ça nous sera utile quand il s'agira de lire nos messages.

En ajoutant le nom au MimeBodyPart, cette fois tout va bien. On retrouve le nom du fichier et son extension qui permet de savoir comment l'ouvrir.

------=_Part_0_1461157241.1361655021999
Content-Type: image/jpeg; name=autruche.jpg
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=autruche.jpg

/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAAHgAA/+4ADkFkb2JlAGTAAAAAAf/b
AIQAEAsLCwwLEAwMEBcPDQ8XGxQQEBQbHxcXFxcXHx4XGhoaGhceHiMlJyUjHi8vMzMvL0BAQEBA
...

------=_Part_0_1461157241.1361655021999
Content-Type: application/octet-stream; name="musique.mp3"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="musique.mp3"

SUQzAwAAAAVbdlRSQ0sAAAADAAAAMTFUQUxCAAAAEQAAAE1hZGFtZSBsYSBHaXJhZmVUQ09OAAAA
EQAAAENoaWxkcmVuknMgTXVzaWNBUElDAAFdGgAAAGltYWdlL3BuZwAAAIlQTkcNChoKAAAADUlI
...

Quant à notre texte, on le retrouve également comme part du message :

------=_Part_0_2141998910.1375532871953
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Texte du message

Si on ajoute quelques caractères accentués, nous obtenons :

------=_Part_0_1167368365.1361657672775
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable

Quelques caract=C3=A8res sp=C3=A9ciaux : =C3=A9 =C3=89 =C3=A0 =C3=80 =E2=82=
=AC =C5=93 =C5=92=20
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectu=

...

Le texte, à cause des caractères accentués, n'est plus codé en us-ascii, mais en quoted-printable.

Maintenant que nous savons un peu à quoi ressemble notre message envoyé, passons à sa réception.

III-B - Lecture du message

Comme pour l'envoi, la réception du message ne change pas, il est simplement un petit peu plus complexe à lire. Nous n'allons donc nous consacrer qu'à sa lecture et son interprétation. On peut aussi reprendre notre méthode printMessage() telle quelle, ça marchera. Le problème, comme nous l'avons vu, est que le texte est en quoted-printable, et nos pièces jointes en Base64. Pas vraiment exploitable dans la console. Nous allons donc devoir séparer chaque partie du message, et les interpréter correctement.

Voyons donc comment récupérer les différentes parties de notre message :

1. public void printMessage(Message message) {

3. try {

4. DataSource dataSource = message.getDataHandler().getDataSource();

5. MimeMultipart mimeMultipart = new MimeMultipart(dataSource);

6. int multiPartCount = mimeMultipart.getCount();

7. System.out.println("Il y a " + multiPartCount + " partie(s) dans ce message.");

9. for (int i = 0; i < multiPartCount; i++ ) {

10. BodyPart bp = mimeMultipart.getBodyPart(i);

11. processBodyPart(bp);

12. }

13. } catch (MessagingException e) {

14. e.printStackTrace();

15. }

16. }

On instancie un objet MimeMultiPart à partir de la DataSource du message. À partir de là, il ne nous reste plus qu'à parcourir les BodyPart pour les traiter :

1. private void processBodyPart(BodyPart bp) {

2. try {

3. System.out.println("Type : " + bp.getContentType());

4. String fileName = bp.getFileName();

5. System.out.println("FileName : " + fileName);

7. if (bp.isMimeType("text/plain")) {

8. System.out.println("Texte du message :");

9. List<String> strings = inputStreamToStrings(bp.getInputStream());

10. for (String string : strings) {

11. System.out.println(string);

12. }

13. } else {

14. File file = new File(PATH_TO_FILE + "/received_" + fileName);

15. FileOutputStream fos = new FileOutputStream(file);

16. bp.writeTo(fos);

17. }

18. } catch (MessagingException | IOException e) {

19. e.printStackTrace();

20. }

21. }

Il n'y a rien de compliqué, il nous suffit de prévoir deux traitements différents : le premier pour le texte du message, qu'on transforme en liste de String puis qu'on imprime, et le second pour les pièces jointes qu'on enregistre en tant que fichiers.

Tout cela semble très bien, mais si on ouvre notre fichier received_autruche.jpg, nous obtenons une erreur. Pour la comprendre, ouvrons-le avec un simple éditeur de texte :

Content-Type: image/jpeg; name=autruche.jpg
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=autruche.jpg

/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAAHgAA/+4ADkFkb2JlAGTAAAAAAf/b
AIQAEAsLCwwLEAwMEBcPDQ8XGxQQEBQbHxcXFxcXHx4XGhoaGhceHiMlJyUjHi8vMzMvL0BAQEBA

Du simple texte, du Base64… Nous avons tout simplement oublié de le convertir. Bien entendu, Java nous met à disposition tout ce dont nous avons besoin. Il s'agit d'un DataHandler, obligeamment fourni par notre BodyPart. Notre code définitif est donc :

DataHandler dh = bp.getDataHandler();
File file = new File(PATH_TO_DATA + "/received_" + fileName);
FileOutputStream fos = new FileOutputStream(file);
dh.writeTo(fos);

Et voilà, nous récupérons notre image et notre musique intactes. Il y a cependant une question qui doit vous tarauder. Nous savions que notre message comportait des pièces jointes. Mais comment savoir si notre message n'est pas un simple texte, sans fioritures, ni images, ni rien ? Eh bien essayons d'envoyer un message comme précédemment. Et voici ce que nous obtenons :

javax.mail.MessagingException: Missing start boundary

Une belle exception. Comment faire alors ? La première idée sera de ne plus composer de message avec message.setText(text). Composons nos messages à partir de MimeMultiPart ne contenant qu'une partie, notre texte, et tout rentre dans l'ordre. Malheureusement, nous ne savons rien à propos du message qui nous a été envoyé. Nous devons agir côté réception.

Utilisons la méthode getContentType() de MimeMultiPart pour observer la différence entre les deux types de messages. Dans le cas d'un message de texte simple, le type est text/plain. Dans le cas d'un message avec pièces jointes (ou un simple texte dans MimeBodyPart à l'intérieur de MimeMultiPart), le type est multipart/mixed.  Ce simple test fera donc l'affaire :

if (message.isMimeType("multipart/mixed")) {
printMixedMessage(message);
} else {
printTextMessage(message);
}

Et voilà, nous sommes capables de lire toutes sortes de messages. Ou presque, il nous reste encore à voir les messages HTML.

IV - Messages HTML

Ni l'envoi, ni la réception et la lecture d'un message HTML ne diffèrent de ceux avec pièces jointes. Nous ne nous intéresserons donc qu'à la création d'un tel message.

Si nous n'avons que du texte, c'est extrêmement simple. Il suffit d'indiquer que le texte est du HTML et non du texte simple :

mimeBodyPart.setText(text, "UTF-8", "html");

ou

message.setText(text, "utf-8", "html");

Et on se retrouve avec un message de type mime text/html, à interpréter, et non plus text/plain, comme auparavant. Très peu de changement donc par rapport aux paragraphes précédents. Maintenant, ajoutons deux images : notre autruche en provenance d'internet, et un sympathique orang-outan en pièce jointe :

Composons le texte du message comme suit :

1. StringBuilder builder = new StringBuilder("<html><body>");

2. for (String string : lines) {

3. builder.append("<H1>Titre du paragraphe</H1><br/>").append(string).append("<br/>");

4. }

6. builder.append("<H1>Autruche</H1><img src=\"").append(AUTRUCHE_URL).append("\"/><br/>");

8. builder.append("<H1>Orang-outan</H1><img src=\"orang-outan.jpg\"/><br/>");

9. builder.append("</body></html>");

11. String message = builder.toString();

Et mettons l'orang-outan en pièce jointe :

File file = new File(PATH_TO_FILE + "/orang-outan.jpg");
FileDataSource datasource1 = new FileDataSource(file);
DataHandler handler1 = new DataHandler(datasource1);
...
MimeBodyPart orangOutan = new MimeBodyPart();
orangOutan.setDataHandler(handler1);
orangOutan.setFileName(datasource1.getName());
mimeMultipart.addBodyPart(orangOutan);

Relevons le courrier avec notre client de mail, et que voyons-nous ? L'autruche est bien à sa place (moyennant peut-être un avertissement du client de mail pour télécharger l'image), mais pas notre orang-outan. Dans Thunderbird, il apparaît comme simple pièce jointe, ce qui ne nous convient pas. En effet, à sa place dans le texte du message, on voit une petite icône indiquant un lien brisé :

Comment dire à notre client de mail qu'il doit afficher ici notre orang-outan ? Simplement avec le type mime du message et le Content-ID de l'image, pour indiquer que l'image fait partie des pièces jointes.

Pour un message avec pièces jointes, le type mime est multipart/mixed. Nous n'avons pas à le préciser, c'est le type par défaut quand nous créons notre objet MimeMultiPart. Pour indiquer qu'une image à afficher dans le corps du message est dans les pièces jointes, on utilisera le type multipart/related, ce qui se fait dans le constructeur :

MimeMultipart mmp = new MimeMultipart("related");

Maintenant, nous pouvons faire référence à nos pièces jointes dans le texte html de notre message, grâce à leur Content-ID :

MimeBodyPart orangOutan = new MimeBodyPart();
orangOutan.setDataHandler(handler1);
orangOutan.setFileName(datasource1.getName());
orangOutan.setContentID("oocid");
mmp.addBodyPart(orangOutan);

Il ne nous reste plus qu'à indiquer la source de notre image via ce content-ID :

builder.append("<H1>Orang-outan</H1><img src=\"cid:oocid\"/><br/>");

Et voilà, quand notre client de messagerie verra cette balise, il saura qu'il doit utiliser l'image avec le content-ID oocid.

Notre message étant en HTML, il est bien entendu possible d'utiliser une feuille de style CSS, des scripts, des vidéos, … Ces éléments peuvent être embarqués avec le message, ou bien se trouver quelque part sur internet. C'est d'ailleurs une astuce utilisée par certains logiciels de mailing (et de SPAM aussi) : ajouter une image de taille réduite, 1 par 1, voire moins, de couleur blanche sur fond blanc, donc invisible, et de compter combien de fois elle a été téléchargée. Si en plus, le nom de l'image est spécifique à chaque message, on sait qui a lu le message. Bien entendu, il faut pour cela que votre client mail accepte les contenus distants. Généralement, il vous demande l'autorisation, car qui dit contenu distant dit potentiellement virus et autres malwares.

Nous ne parlerons pas ici de comment recevoir et lire un message HTML. C'est exactement la même chose que pour un message avec pièces jointes, au type mime près.

V - Conclusion

Nous avons vu qu'envoyer et recevoir des mails avec Java n'est pas très compliqué, il suffit juste de bien suivre les étapes. Ce qui est un peu plus délicat, et qui demande donc de bien faire attention, c'est l'interprétation du contenu des messages, notamment au niveau des types MIME.

Maintenant que vous savez tout, il ne vous reste plus qu'à créer votre propre webmail, et essayer de concurrencer GoogleMail.

VI - Remerciements

Je tiens à remercier tous ceux qui sont intervenus dans la rédaction de cet article, que ce soit en tant que critiques ou autre : Deepin, ZouBi, Mickael Baron, thierryler, et ced.

VII - Liens

Si vous voulez aller plus loin dans l'envoi de mail avec Java, voici quelques liens utiles :

- documentation officielle : au format pdf, elle vous permettra découvrir toutes les possibilités non présentées ici, comme l'utilisation de listeners sur différents objets, permettant de réagir aux événements les concernant ;

- API de Javamail : toujours utile ;

- les articles d'origine sur mon blog : Javamail1, Javamail2, Javamail3 ;

- Apache Commons Mail : une API de la Fondation Apache, qui se place au-dessus de javaMail, et qui facilite l'utilisation de cette dernière. Je ne la connais pas, mais je compte mettre à jour cet article un jour en y ajoutant son support.