Недавно работая над экспериментом на Android, я хотел отправлять электронные письма, используя SMTP-сервер, используя аутентификацию и шифрование, из приложения для Android.
Что ж, я обнаружил, что javax.mail на Android не очень хороший вариант , так как он зависит от классов awt (я думаю, это унаследовано); некоторые люди пытались адаптировать его так, чтобы вам не требовался весь пакет awt , но у меня это не получилось ; не говоря уже о тех людях, которые несколько лет назад самостоятельно переделали javax.mail для Android без какого-либо обслуживания.
Еще один вариант, который мне пришёл в голову, — это использование Apache Commons Net : поскольку сообщество добавило SMTPSClient и AuthenticatingSMTPClient к исходному SMTP-клиенту ( и применил небольшой мой патч для SSL и аутентификации ), вы можете встроить эту библиотеку в свой Android приложение (не требуется транзитивных зависимостей) для отправки почты с использованием аутентификации через защищенный уровень. ( этот пост действительно вдохновил меня , но он использует старую версию Apache Commons Net, используя 3.3, вам больше не нужно это делать)
SMTP-аутентификация и STARTTLS с Commons Net
Обычно в этом случае используется порт 25 или альтернативный порт 587. Вы подключаетесь к SMTP-серверу по простому соединению, запрашиваете доступные команды, если поддерживается STARTTLS, вы используете его, а остальная часть связи шифруется.
Давайте рассмотрим пример gmail, поскольку smtp.gmail.com поддерживает аутентификацию и STARTTLS.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
public void sendEmail() throws Exception { String hostname = "smtp.gmail.com"; int port = 587; String password = "gmailpassword"; String login = "account@gmail.com"; String from = login; String subject = "subject" ; String text = "message"; AuthenticatingSMTPClient client = new AuthenticatingSMTPClient(); try { String to = "recipient@email.com"; // optionally set a timeout to have a faster feedback on errors client.setDefaultTimeout(10 * 1000); // you connect to the SMTP server client.connect(hostname, port); // you say ehlo and you specify the host you are connecting from, could be anything client.ehlo("localhost"); // if your host accepts STARTTLS, we're good everything will be encrypted, otherwise we're done here if (client.execTLS()) { client.auth(AuthenticatingSMTPClient.AUTH_METHOD.LOGIN, login, password); checkReply(client); client.setSender(from); checkReply(client); client.addRecipient(to); checkReply(client); Writer writer = client.sendMessageData(); if (writer != null) { SimpleSMTPHeader header = new SimpleSMTPHeader(from, to, subject); writer.write(header.toString()); writer.write(text); writer.close(); if(!client.completePendingCommand()) {// failure throw new Exception("Failure to send the email "+ client.getReply() + client.getReplyString()); } } else { throw new Exception("Failure to send the email "+ client.getReply() + client.getReplyString()); } } else { throw new Exception("STARTTLS was not accepted "+ client.getReply() + client.getReplyString()); } } catch (Exception e) { throw e; } finally { client.logout(); client.disconnect(); } } private static void checkReply(SMTPClient sc) throws Exception { if (SMTPReply.isNegativeTransient(sc.getReplyCode())) { throw new Exception("Transient SMTP error " + sc.getReply() + sc.getReplyString()); } else if (SMTPReply.isNegativePermanent(sc.getReplyCode())) { throw new Exception("Permanent SMTP error " + sc.getReply() + sc.getReplyString()); } |
Здесь нечего добавить, конечно, обработка исключений может быть оптимизирована, если вы используете свои собственные классы исключений.
SMTP-аутентификация и SSL с Commons Net
Некоторые SMTP-серверы настроены на прием только «a to z SSL»: вы должны защитить связь прямо перед тем, как отправлять какие-либо команды серверу; обычно используется порт 465.
Давайте возьмем пример LaPoste.net (бесплатные почтовые аккаунты, предлагаемые французской почтой):
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
public void sendEmail() throws Exception { String hostname = "smtp.laposte.net"; int port = 465; String password = "password"; String login = "firstname.lastname"; String from = login + "@laposte.net"; String subject = "subject" ; String text = "message"; // this is the important part : you tell your client to connect using SSL right away AuthenticatingSMTPClient client = new AuthenticatingSMTPClient("TLS",true); try { String to = "anthony.dahanne@gmail.com"; // optionally set a timeout to have a faster feedback on errors client.setDefaultTimeout(10 * 1000); client.connect(hostname, port); client.ehlo("localhost"); client.auth(AuthenticatingSMTPClient.AUTH_METHOD.LOGIN, login, password); checkReply(client); client.setSender(from); checkReply(client); client.addRecipient(to); checkReply(client); Writer writer = client.sendMessageData(); if (writer != null) { SimpleSMTPHeader header = new SimpleSMTPHeader(from, to, subject); writer.write(header.toString()); writer.write(text); writer.close(); if(!client.completePendingCommand()) {// failure throw new Exception("Failure to send the email "+ client.getReply() + client.getReplyString()); } } else { throw new Exception("Failure to send the email "+ client.getReply() + client.getReplyString()); } } catch (Exception e) { throw e; } finally { client.logout(); client.disconnect(); } |
Я не повторял здесь метод checkReply (), так как он одинаков для обоих фрагментов кода; вы заметите, что использование SSL сразу означает, что вам не нужно проверять ответ execTls () (на самом деле он не будет работать, если вы это сделаете).
Завершение
Это об этом; если вы хотите, чтобы эти примеры работали в вашей среде, вы можете добавить apache commons net 3.3 jar в ваш classpath
Если вы используете Maven, добавьте зависимость:
|
1
2
3
4
5
|
<dependency> <groupid>commons-net</groupid> <artifactid>commons-net</artifactid> <version>3.3</version></dependency> |
Если вы используете Gradle для своего проекта Android, вы также можете использовать следующий файл build.gradle:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
buildscript { repositories { mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:0.4.2' }}apply plugin: 'android' repositories { mavenCentral()} dependencies { compile fileTree(dir: 'libs', include: '*.jar'), 'commons-net:commons-net:3.3'} android { compileSdkVersion 17 buildToolsVersion "17.0.0" sourceSets { main { manifest.srcFile 'AndroidManifest.xml' java.srcDirs = ['src'] resources.srcDirs = ['src'] aidl.srcDirs = ['src'] renderscript.srcDirs = ['src'] res.srcDirs = ['res'] assets.srcDirs = ['assets'] } instrumentTest.setRoot('tests') }} |
Наслаждайтесь !