Недавно работая над экспериментом на 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 from = login; String subject = "subject" ; String text = "message" ; AuthenticatingSMTPClient client = new AuthenticatingSMTPClient(); try { // 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 { // 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' ) } } |
Наслаждайтесь !