Archiwa tagu: mail

JavaMail API i rozpoznawanie zwrotek

Wprowadzenie

Wysyłanie e-maili to funkcja, bez której większość współczesnych systemów informatycznych nie mogłoby się obejść. Chyba wszystkie liczące się środowiska programistyczne oferują mechanizmy ułatwiające realizację takiej funkcjonalności. W przypadku języka Java jest to specyfikacja JavaMail API (JSR 919). Nie będzie chyba zbytnią przesadą stwierdzenie, że w sieci można znaleźć tysiące artykułów pokazujących, jak wysyłać i odbierać wiadomości przy użyciu JavaMail, dzięki czemu implementacja tych funkcji we własnym systemie zwykle nie sprawia problemów. Istnieje jednak pewien aspekt, którego próżno szukać we wspomnianych artykułach i tego właśnie dotyczy niniejszy artykuł. Chodzi o metodę rozpoznawania faktu, czy wysłany mail doszedł, czy nie.

Wiem, wiem, nie ma niezawodnej metody realizacji takiego zadania. Nigdy nie będziemy mieć gwarancji, że wysłany przez nas e-mail doszedł do adresata, a tym bardziej że został przez niego przeczytany. W pewnych okolicznościach możemy mieć jednak pewność, że e-mail nie doszedł, a mianowicie wtedy, kiedy dostaniemy tak zwaną zwrotkę (bounce message), czyli automatyczną odpowiedź od serwera docelowego (lub czasami naszego) o niemożności dostarczenia przesyłki.

Jak rozpoznać zwrotkę?

Rozpoznanie takiej zwrotki bywa problematyczne, nie ma na to prostej metody. Serwery pocztowe stosują różne formaty przy wysyłaniu tego rodzaju komunikatów, tak więc analiza tytułu czy treści wiadomości będzie mało wiarygodna. Dodatkowo, jeżeli na dany adres oprócz zwrotek przychodzi dużo innych wiadomości, taka analiza może być bardzo kosztowna pod względem wydajności. Optymalnie byłoby, gdyby zwrotki przychodziły na specjalnie w tym celu wydzielony adres. Na szczęście jest na to sposób.

Jak wiadomo, każda wiadomość e-mail zawiera pola From: i To:. W pierwszym z nich znajduje się adres nadawcy, a w drugim – adres odbiorcy. Nie wszyscy jednak wiedzą, że w transmisji SMTP wykorzystywane jest jeszcze jedno pole (a w zasadzie polecenie) zawierające adres nadawcy, tzw. Envelope from. To pierwsze pole From: jest przeznaczone dla programów klienckich (czyli programów pocztowych na naszych komputerach), natomiast to drugie, Envelope From, jest używane w trakcie transmisji pomiędzy agentami pocztowymi, czyli serwerami i stanowi część protokołu SMTP1. To właśnie Envelope From mówi serwerowi, gdzie ma wysłać ewentualną zwrotkę. Zwykle oba pola From zawierają ten sam adres nadawcy, jednak nie musi tak być. Jeżeli w polu Envelope From ustawimy jakiś inny, specjalnie przygotowany adres, to tam właśnie będą wysyłane zwrotki. Jeżeli taki specjalny adres będziemy używać tylko w tym jednym celu, to będziemy mogli mieć niemal pewność, że wszystkie wiadomości, które tam dochodzą, to zwrotki.

Pole „Envelope From” w JavaMail API

Specyfikacja JavaMail API umożliwia programistom ustawienie pola Envelope From. Służy do tego metoda SMTPMessage.setEnvelopeFrom(String from). Klasa SMTPMessage jest implementacją klasy MimeMessage. Mimo że nie należy do oficjalnego API, z dokumentacji wynika, że można jej śmiało używać w miejsce standardowej MimeMessage.

Poniżej przykład zaczerpnięty z dokumentacji JavaMail API:

    Properties props = new Properties();
    props.put("mail.smtp.host", "my-mail-server");
    Session session = Session.getInstance(props, null);

    try {
        SMTPMessage msg = new SMTPMessage(session);
        msg.setEnvelopeFrom("return-address@example.com");
        msg.setFrom("me@example.com");
        msg.setRecipients(Message.RecipientType.TO,
                          "you@example.com");
        msg.setSubject("JavaMail hello world example");
        msg.setSentDate(new Date());
        msg.setText("Hello, world!\n");
        Transport.send(msg, "me@example.com", "my-password");
    } catch (MessagingException mex) {
        System.out.println("send failed, exception: " + mex);
    }

Dla kompletności informacji dodam, że istnieje drugi sposób ustawienia tej wartości, ale mniej elastyczny. Powyższy sposób pozwala ustawić inny adres dla każdej wysłanej wiadomości. Ten, który przedstawiłem niżej, ustawia ten sam adres dla wszystkich wiadomości wysłanych w ramach jednej sesji.

    Properties props = new Properties();
    props.put("mail.smtp.host", "my-mail-server");
    props.put("mail.smtp.from", "return-address@example.com");
    Session session = Session.getInstance(props, null);

    try {
        MimeMessage msg = new MimeMessage(session);
        msg.setFrom("me@example.com");
        msg.setRecipients(Message.RecipientType.TO,
                          "you@example.com");
        msg.setSubject("JavaMail hello world example");
        msg.setSentDate(new Date());
        msg.setText("Hello, world!\n");
        Transport.send(msg, "me@example.com", "my-password");
    } catch (MessagingException mex) {
        System.out.println("send failed, exception: " + mex);
    }

Finał

Problem rozpoznawania zwrotek mamy w ten sposób załatwiony. Pozostaje jeszcze jeden nietrywialny problem: dopasowania zwrotki do oryginalnej wiadomości. To jednak temat na inny artykuł.


  1. Jak już wspomniałem, jest to nie tyle pole, co polecenie protokołu SMTP. Serwer nadawcy wysyła je do serwera docelowego niedługo po nawiązaniu połączenia. Polecenie to wygląda tak:
    MAIL FROM: <return-address@example.com>