воскресенье, 9 августа 2009 г.

Несколько мгновений JavaMail + Gmail


И вот пришло время Java. Первой моей задачей было написать модуль обрабатывающий входящие письма из нужного почтового ящика, и по необходимости отправлял ответ. Погуглив пару минут сразу было найдено основа для моего модуля - JavaMail. Немного магии присутствует и в этой сборке.

Через некоторое время у меня были методы для работы с почтой.

Для отправки почты:
String SMTP_AUTH_USER = "<почтовый адрес в почте Gmail>"; 
String SMTP_AUTH_PWD = "<пароль>";

Properties props = new Properties();

props.put("mail.transport.protocol", "smtps");
props.put("mail.smtps.host", SMTP_AUTH_USER);
props.put("mail.smtps.auth", "true");
props.put("mail.smtp.sendpartial", "true");

Session session = Session.getDefaultInstance(props);
session.setDebug(true);
Transport transport = session.getTransport();
transport.connect("smtp.gmail.com", 465, SMTP_AUTH_USER, SMTP_AUTH_PWD);

MimeMessage message = new MimeMessage(session);
message.setSubject("тестовое письмо!");
message.setText("Тестирование JavaMail c Gmail почтой прошло успешно!");
message.addRecipient(Message.RecipientType.TO, new InternetAddress("<получатель>"));
message.setSentDate(new Date());

transport.sendMessage(message, message.getRecipients(Message.RecipientType.TO));
Для получения почты:
String POP_AUTH_USER = "<почтовый адрес в почте Gmail>";
String POP_AUTH_PWD = "<пароль>";

String FOLDER_INDOX = "INBOX"; // имя папки "Входящие"
String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";

Properties pop3Props = new Properties();

pop3Props.setProperty("mail.pop3.socketFactory.class", SSL_FACTORY);
pop3Props.setProperty("mail.pop3.socketFactory.fallback", "false");
pop3Props.setProperty("mail.pop3.port", "995");
pop3Props.setProperty("mail.pop3.socketFactory.port", "995");

URLName url = new URLName("pop3", "pop.gmail.com", 955, "", POP_AUTH_USER, POP_AUTH_PWD);

Session session = Session.getInstance(pop3Props, null);
Store store = session.getStore(url);
store.connect();

Folder folder = store.getFolder(FOLDER_INDOX);
try {
folder.open(Folder.READ_WRITE);
} catch (MessagingException ex) {
folder.open(Folder.READ_ONLY);
}
Message[] messages = folder.search(new FlagTerm(new Flags(Flags.Flag.SEEN), false));
// обработка сообщений
folder.close(false);
store.close();
В переменной messages буду содержаться список новых писем. Все вроде бы ясно и понятно, делаем следующий шаг.

 

Как ответить на письмо?

Почитав FAQ по JavaMail стало понятно что рекомендуют воспользоваться методов Message.reply(). И вот тут начала проявляться магия JavaMail.
Message[] mes = folder.getMessages();
for(int i = 0; i < mes.length; i++) {
Message message = mes[i];
Message reply = message.reply(false);
reply.setText("Ответ на ваш запрос! :)");
// отправить его нужно по протоколу SMTP! Делаем финт ушами.
Transport transport = pop.getSession().getTransport("smtps");
transport.connect("smtp.gmail.com", 465, "<почтовый адрес в почте Gmail>", "<пароль>");
transport.sendMessage(reply, reply.getRecipients(Message.RecipientType.TO));
transport.close();
break;
}

 

Как удалить сообщение используя POP протокол?

После получения письма иногда возникает необходимость удалить его, отметить как не прочитанное, и т.д. Что бы сохранить эти изменения нужно воспользоваться очередным магическим функционалом, а именно закрыть папку с параметром true.
message.setFlag(Flag.DELETED, true);
// ......
folder.close(true);

 

Как получить доступ к папкам "Все письма", "Спам", и т.д.

Тут возникло некое разочарование, т.к. Gmail не дает доступ через smtp протокол к таким папкам. Для доступа в такие папки необходимо воспользоваться протоколом imap (провайдер "imaps") и получить папку с указанием нужного имени. Например:
store.getFolder("[Gmail]/All"); //Вся почта
store.getFolder("[Gmail]/Spam"); //Спам
store.getFolder("[Gmail]/Starred"); //Помеченные
store.getFolder("[Gmail]/Sent"); //Отправленные
store.getFolder("[Gmail]/Drafts"); //Черновики
store.getFolder("[Gmail]/Trash"); //Корзина


Радует в работе с JavaMail то, что FAQ написан очень хорошо и качественно. В 80% случаев решение мелких проблем можно было найти в нем.
Модуль написан, задание выполнено :).

12 комментариев:

  1. Спасибо большое!
    Долго искала работающий код и вот у вас нашла!!!

    ОтветитьУдалить
  2. Полезная статья, тоже искал хороший пример по JavaMail. Спасибо

    ОтветитьУдалить
  3. Пользуйтесь на здоровье!

    ОтветитьУдалить
  4. с чем связана ошибка:
    javax.mail.MessagingException: can't determine local email address

    ОтветитьУдалить
  5. >javax.mail.MessagingException: can't determine local email address

    Возникает в случае если не задан отправитель! Если проблема не в этом (или не получится разобраться), то код в студию.

    ОтветитьУдалить
    Ответы
    1. Спасибо, помогло в реализации javamail под Андроид.

      message.setFrom(new InternetAddress("Anything"));

      Удалить
  6. Скажите, а можно ли получить заголовки писем? К примеру есть много писем с большим кол-вом вложений. не рационально скачивать их все. Необходимо сначала получить заголовки.

    ОтветитьУдалить
    Ответы
    1. Большинство pop3 серверов поддерживают такую возможность (обратите внимание на команду TOP). В этом случае нашел ссылку на то, что JavaMail работает с серверами через эту команду, но т.к. кода под рукой нет, то готовый пример не смогу сделать.
      Если разберетесь, то буду благодарен за готовый пример кода.

      Удалить
  7. Как удалить сообщение используя POP протокол?
    Все сделал как указано здесь
    message.setFlag(Flag.DELETED, true);
    folder.close(true);
    Тогда при запросе через POP3 писем нет (будто удалились) но если зайти на сайт gmail.com там все якобы "удаленные" письма видны в папке "Входящие". Почему письма удаляются для запроса POP3 но остаются физически на почтовом сервере?
    Как удалять письма полностью с почтового сервера.

    ОтветитьУдалить
    Ответы
    1. Скорей всего это особенность поведения Gmail. Думаю, проблему можно решить через imap с помощью перемещения писем между папками.

      Удалить
    2. удаляются через протокол imap, pop3 сейчас не удаляет

      Удалить