###### tags: `JSF_教學`
# 訓練課程 18 (t18) - 寄送 email: 使用 JavaMail
## SMTP Server 工作原理介紹

Ref: [25, 2525, 465, 587, and Other Numbers: All About SMTP Ports | blog.mailtrap.io](https://blog.mailtrap.io/smtp-ports-25-465-587-used-for/)
**SMTP/SMTPs (Simple Mail Transfer Protocol/Secure or SMTP over SSL)**: This is a protocol for sending email messages between servers.
**SSL (Secure Sockets Layer)** and its successor, **Transport Layer Security (TLS)**, provide a way to encrypt a communication channel between two computers over the Internet.
**STARTTLS** is an email protocol command that tells an email server that an email client wants to turn an existing insecure connection into a secure one.
Because TLS and SSL are application-layer protocols, senders and receivers need to know that they are being used to encrypt emails during transit. That’s where STARTTLS comes into play.
Ref: [Understanding SSL, TLS, and STARTTLS Email Encryption | www.sparkpost.com](https://www.sparkpost.com/resources/email-explained/ssl-tls-starttls-encyption/#:~:text=STARTTLS%20is%20an%20email%20protocol,connection%20into%20a%20secure%20one)
## SMTP Server 的設定
### mailtrap.io
Safe Email Testing for Staging & Development
重要功能:
- Test HTML for support by basic email clients:
Inspect and debug each part of your message, and check if it works fine for the most popular email clients.
- Share inboxes with your team members
Discuss your ideas with colleagues or demonstrate your progress to the customer: invite them to your inbox!
Client 端 SMTP 參數:
- host: "smtp.mailtrap.io"
- port: 2525
- ssl: no
- tls: yes
- user:
- password:

Free Plan features

### gmail smtp 設定
使用 SMTPS 協定寄送郵件
設定步驟
1. 申請 gmail 帳號
2. 啟用二步驟驗證

3. 產生應用程式密碼

4. 使用應用程式密碼進行身份鑑別
SMTPS 參數:
- 外寄郵件 (SMTP) 伺服器: smtp.gmail.com
- 需要安全資料傳輸層 (SSL):是
- 需要驗證:是
- 安全資料傳輸層 (SSL) 通訊埠:465
- 帳戶名稱: 完整電子郵件地址
- 密碼: 應用程式密碼
Ref: https://support.google.com/mail/answer/7126229?hl=zh-Hant
程式碼參考:
https://stackoverflow.com/questions/1990454/using-javamail-to-connect-to-gmail-smtp-server-ignores-specified-port-and-tries
## JavaMail API
### API 操作流程
1. 放置 SMTP/SMTPS 相關參數到 [`java.util.Properties`](https://docs.oracle.com/javase/tutorial/essential/environment/properties.html) 物件
2. 提供先前的 `Properties` 物件及帳密, 存放到 [`javax.mail.Session`](https://javaee.github.io/javamail/docs/api/javax/mail/Session.html) 物件.
3. 建立 [`javax.mail.Message`](https://javaee.github.io/javamail/docs/api/javax/mail/Message.html) 物件, 代表要傳送的 email 訊息。需要提供的資料:
- javax.mail.Session 物件
- Mail recipient (收件人)
- Mail From (寄件人)
- Mail Subject 郵件標題
- Mail Content 郵件內容(文字/HTML)
4. 使用 `javax.mail.Transport` 物件傳送郵件
### Example: 使用 SMTP + TLS 協定寄送郵件
1. 放置 SMTP/SMTPS 相關參數到 [`java.util.Properties`]
Ref: [SMTP properties](https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html)
SMTP properties
```java=
Properties prop = new Properties();
prop.put("mail.smtp.host", "smtp.exmple.com");
prop.put("mail.smtp.port", "587");
prop.put("mail.smtp.auth", "true");
prop.put("mail.smtp.starttls.enable", "true"); //TLS
```
SMTPS properties
```java=
prop.put("mail.smtp.auth", "true");
prop.put("mail.smtp.startls.enable", "true");
// default host
prop.put("mail.smtp.host", smtps.example.com);
// default TLS port
prop.put("mail.smtp.port", "465");
// prop.put("mail.smtp.debug", "true");
prop.put("mail.smtp.socketFactory.port", "465");
// Concrete object to create SMTP sockets
prop.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
//If set to true, failure to create a socket using the specified socket factory class will cause the socket to be created using the java.net.Socket class. Defaults to true.
prop.put("mail.smtp.socketFactory.fallback", "false");
```
2. 提供先前的 `Properties` 物件及帳密, 存放到 [`javax.mail.Session`](https://javaee.github.io/javamail/docs/api/javax/mail/Session.html) 物件.
```java=
// Need to provide SMTP Host properties, username and password
Session session = Session.getInstance(prop,
// An anonymous sub-Class of Authenticator
// The getPasswordAuthentication() is called when authenticating
new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
```
使用 Session.getInstance() 方法取得 Session 物件:
```java
static Session getInstance(Properties props, Authenticator authenticator)
```
Username 及 Password 要放到 ['import javax.mail.PasswordAuthentication'](https://javaee.github.io/javamail/docs/api/index.html?javax/mail/PasswordAuthentication.html) 物件中。
Applications use this class by creating a subclass, and registering an instance of that subclass with the session (`javax.mail.Session` Object) when it is created.
When authentication is required, the system will invoke a method on the subclass (like getPasswordAuthentication).
3. 建立 [`javax.mail.Message`]
```java=
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress("from@gmail.com"));
message.setRecipients(
Message.RecipientType.TO,
InternetAddress.parse("to_username_a@gmail.com, to_username_b@yahoo.com")
);
message.setSubject("Testing Gmail TLS");
message.setText("Hello, I'm testing the mail");
```
`javax.mail.Message` 為抽象類別; 使用 [`javax.mail.internet.MimeMessage`](https://javaee.github.io/javamail/docs/api/javax/mail/internet/MimeMessage.html#MimeMessage-javax.mail.Session-) 實作類別建立 `Message` 物件。
電子郵件地址使用 [`javax.mail.internet.InternetAddress`](https://javaee.github.io/javamail/docs/api/javax/mail/internet/InternetAddress.html) 物件表示。
依據 RFC822 標準, Internet Address 的字串表示方式為: `"user@host.domain"` or `"Personal Name <user@host.domain>"`.
4. 使用 `javax.mail.Transport` 物件傳送郵件
SMTP
```java=
Transport.send(message, message.getAllRecipients());
```
SMTPS
```java=
Transport transport = session.getTransport("smtps");
transport.sendMessage(message, message.getAllRecipients());
transport.close();
```
## 實作 1 寄信到 mailtrap.io
### 1. 申請 mailtrap.io 帳號
### 2. Clone the project
https://github.com/hychen39/easymail.git
### 3. 編譯專案成 jar 檔
Compile, Test, and Package
```
mvn package
```
Compile and Package
```
mvn package -Dmaven.test.skip=true
```
Ref: https://stackoverflow.com/questions/7456006/maven-package-install-without-test-skip-tests
Output file: `target/easymail-1.0.jar`
### 4. 註冊 jar 檔到 Maven Local Repository
```
mvn install:install-file -Dfile=target/easymail-1.0.jar -DpomFile=./pom.xml
```
```
$ mvn install:install-file -Dfile=target/easymail-1.0.jar -DpomFile=./pom.xml
[INFO] Scanning for projects...
[INFO]
[INFO] -----------------------< org.hychen39:easymail >------------------------
[INFO] Building easymail 1.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-install-plugin:2.5.2:install-file (default-cli) @ easymail ---
[INFO] Installing D:\temp\easymail\target\easymail-1.0.jar to C:\Users\user\.m2\repository\org\hychen39\easymail\1.0\easymail-1.0.jar
[INFO] Installing D:\temp\easymail\pom.xml to C:\Users\user\.m2\repository\org\hychen39\easymail\1.0\easymail-1.0.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.878 s
[INFO] Finished at: 2020-11-07T15:07:02+08:00
[INFO] ------------------------------------------------------------------------
```
Ref: https://maven.apache.org/guides/mini/guide-3rd-party-jars-local.html
### 5. 建立 Maven Java 專案, 並加入 Dependency
建立一個 Maven Java Project.
加入以下的 dependency:
```xml=
<dependencies>
<dependency>
<groupId>org.hychen39</groupId>
<artifactId>easymail</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
```

### 6. 使用 org.hychen39.easymail package 撰寫 email 程式
```java=
public class App {
public static void main(String[] args){
String username = "your_username";
String password = "your_password";
// Prepare the EmailEntity
EmailEntity email = new EmailEntity();
email.setRecipient("user1@example.com")
.setSender("usedbook106@gmail.com")
.setUsername(username)
.setPassword(password)
.setSubject("Testing the java mail api")
.setContents("Testing mailtrap.io smtp");
//Create the SMTP Host Properties
Properties props = SmtpHostPropertyBuilder.getStmpProp("smtp.mailtrap.io", "2525");
MailByServerProtocal mailByMailtrapSMTP = new MailByMailtrapSMTP();
mailByMailtrapSMTP.setEmailEntity(email).setSmtpHostProperties(props);
if ( mailByMailtrapSMTP.sendMail())
System.out.println("Mail successfully");
else
System.out.println("Mail failed");
}
}
```
## References:
1. [JavaMail API – Sending email via Gmail SMTP example](https://mkyong.com/java/javamail-api-sending-email-via-gmail-smtp-example/)
1. [Jakarta Mail Tutorial](https://blog.mailtrap.io/jakarta-mail-tutorial/)
2. [JavaMail Gmail SMTP服務器](http://tw.gitbook.net/javamail_api/javamail_api_gmail_smtp_server.html)
3. [Sending an Email using the JavaMail API](https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/javamail/javamail.html)
4. [Jakarta Mail API documentation](https://eclipse-ee4j.github.io/mail/docs/api/)
5. [沒有mail server怎麼測試寄送email?快放過你的gmail來看看有那些可以測試用的smtp mail server](https://blog.alantsai.net/posts/2017/12/test-smtp-server-for-sending-mail-in-development)