# Email Alert 架構分析 ## 概述 RAIDGuard X 支援透過 SMTP 發送郵件通知功能,包括: - 手動發送測試郵件 - 事件觸發自動郵件通知 ## 架構流程 ``` ┌─────────────────────────────────────────────────────────────────────┐ │ Email Alert 架構 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ GUI │ │ Controller │ │ SMTP Server │ │ │ │ frmEmail │◄──────►│ (Port 8922)│ │ │ │ │ └──────┬───────┘ └──────────────┘ └──────┬───────┘ │ │ │ │ │ │ │ In-Band API (OpCode 0x01) │ │ │ │ - SMTP 伺服器 │ │ │ │ - SMTP 使用者名稱/密碼 │ │ │ │ - 寄件人/收件人 │ │ │ │ - 啟用事件通知 │ │ │ └────────────────────────┬───────────────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ JavaMail API (javax.mail) │ │ │ │ - Session.getInstance() │ │ │ │ - MimeMessage │ │ │ │ - Transport.send() │ │ │ └──────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘ ``` ## 設定介面 (frmEmail) ``` ┌─────────────────────────────────────────────────────────────────┐ │ Email Settings │ ├─────────────────────────────────────────────────────────────────┤ │ Mailing List: [Send Test Email] │ │ ┌─────────────────────────────────────────┐ │ │ │ user1@example.com │ [Remove] │ │ │ user2@example.com │ │ │ │ │ │ │ └─────────────────────────────────────────┘ │ │ │ │ SMTP Settings: │ │ ┌───────────────────────────────────────────────────────────┐ │ │ │ Mail Server (SMTP): [smtp.example.com ] │ │ │ │ From Email: [sender@example.com ] │ │ │ │ ☐ SMTP Auth Username: [__________] │ │ │ │ Password: [__________] │ │ │ │ ☐ Post Event to Email (Enable auto-alert) │ │ │ └───────────────────────────────────────────────────────────┘ │ │ │ │ [OK] [Cancel] │ └─────────────────────────────────────────────────────────────────┘ ``` ## 儲存的設定 透過 In-Band API 儲存到控制器: | OpCode | 說明 | |--------|------| | 0x07 | 更新 SMTP 伺服器名稱 | | 0x09 | 更新 SMTP 使用者名稱 | | 0x0A | 更新 SMTP 密碼 | | 0x0B | 取得 SMTP 使用者名稱 | | 0x0C | 取得 SMTP 密碼 | | 0x05 | 取得 SMTP 伺服器名稱 | | 0x0E | 儲存 Post Event 設定 | | 0x19 | 取得 Post Event 設定 | ## 程式碼分析 ### 1. 郵件發送 (frmEmail.java) ```java // 使用 JavaMail API import javax.mail.*; import javax.mail.internet.*; public void jbSendMail_actionPerformed(ActionEvent e) { // 建立 Session Properties props = new Properties(); props.put("mail.smtp.host", this.jtServerIp.getText()); if (jcSMTPCheck.isSelected()) { // SMTP 認證 props.put("mail.smtp.auth", "true"); props.put("mail.smtp.user", sUsername); props.put("mail.smtp.password", sPassword); session = Session.getInstance(props, new Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(sUsername, sPassword); } }); } else { // 無認證 props.put("mail.smtp.auth", "false"); session = Session.getInstance(props); } // 建立郵件 Message msg = new MimeMessage(session); msg.setFrom(new InternetAddress(sNameMailFrom)); msg.setSubject(sMailTitle); msg.setText(sMailBody); msg.setSentDate(new Date()); // 發送到所有收件人 for (int j = 0; j < dmMailList.getRowCount(); j++) { sNameMailTo = dmMailList.getValueAt(j, 0).toString().trim(); if (!sNameMailTo.equals("")) { msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(sNameMailTo, false)); Transport.send(msg); // 發送郵件 } } } ``` ### 2. SMTP 認證設定 ```java // 有認證 if (jcSMTPCheck.isSelected()) { props.put("mail.smtp.auth", "true"); props.put("mail.smtp.ssl.enable", "true"); // SSL session = Session.getInstance(props, new Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(username, password); } }); } // 無認證 else { props.put("mail.smtp.host", smtpServer); props.put("mail.smtp.auth", "false"); session = Session.getInstance(props); } ``` ### 3. 事件通知開關 ```java // 儲存 Post Event 設定到控制器 bCmds[2] = 0x0E; // OpCode if (jcPostEvent.isSelected()) { bCmds[4] = 1; // Enable } else { bCmds[4] = 0; // Disable } // 透過 CmdQueue 發送到控制器 CmdQueue.NewCommand(ip, sn, 1, 1, 0x01); ``` ## 依賴庫 ```xml com.sun.mail javax.mail 1.6.2 ``` ## Polling 與事件觸發 ``` ┌─────────────────────────────────────────────────────────────────┐ │ Polling Timer (定時輪詢) │ │ - wkPolling1: 每 12 秒 │ │ - wkPolling2: 每 30 秒 │ │ - wkEventPolling: 每 18 秒 │ │ - wkEventPollingForSNMP: 每 22 秒 │ └──────────────────────────┬──────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 檢測事件 │ │ 1. 取得控制器事件 (Page 0x24) │ │ 2. 比對新事件 │ │ 3. 觸發通知 │ └──────────────────────────┬──────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 通知方式 │ │ - SNMP Trap (frmMain.EventPollingForSNMP) │ │ - Email Alert (如果 jcPostEvent 啟用) │ │ - UI Event List 更新 │ └─────────────────────────────────────────────────────────────────┘ ``` ## 關鍵類 | 類別 | 檔案 | 功能 | |------|------|------| | `frmEmail` | frmEmail.java | 郵件設定介面 | | `dlgServer` | dlgServer.java | Server 設定 (包含 SMTP) | | `PageDataDB` | PageDataDB.java | 郵件設定儲存 | | `SNMPSender` | SNMP/SNMPSender.java | SNMP Trap 發送 | ## Code Flow ### 1. 設定 SMTP ``` frmEmail (開啟) → 輸入 SMTP 伺服器 → 輸入寄件人/收件人 → (可選) 啟用 SMTP 認證 → (可選) 啟用 Post Event → OK → 儲存到控制器 (OpCode 0x07/0x09/0x0A/0x0E) ``` ### 2. 發送測試郵件 ``` 點擊 [Send Test Email] → frmEmail.jbSendMail_actionPerformed() → 建立 Session (JavaMail) → 發送到所有收件人 → 顯示成功/失敗訊息 ``` ### 3. 事件自動通知 ``` Polling 偵測到新事件 → 檢查 jcPostEvent 是否啟用 → 組建郵件內容 (事件描述) → 發送到所有收件人 ``` ## 郵件格式 ``` Subject: RAIDGuard X Alert - {Controller Name} Body: Controller: {SN} Event: {Event Description} Time: {Timestamp} Severity: {Info/Warning/Error} ``` ## 安全考量 1. **密碼儲存**: SMTP 密碼儲存在控制器端,可能有安全風險 2. **傳輸加密**: 支援 SSL/TLS (jcSMTPCheck) 3. **認證**: 支援 SMTP AUTH (PLAIN/LOGIN) ## Rust 實作建議 ```rust // 使用 lettre crate use lettre::{SmtpClient, Transport, Message, SendableEmail}; use lettre::smtp::authentication::Credentials; pub fn send_email( smtp_server: &str, username: &str, password: &str, from: &str, to: &str, subject: &str, body: &str, use_auth: bool, use_tls: bool, ) -> Result<(), Box> { let email = Message::builder() .from(from.parse()?) .to(to.parse()?) .subject(subject) .body(body.to_string())?; let mut smtp = SmtpClient::new_localhost()?; if use_tls { smtp = SmtpClient::new_new_ssl(smtp_server)?; } else { smtp = SmtpClient::new_new(smtp_server)?; } if use_auth { let creds = Credentials::new(username.to_string(), password.to_string()); smtp = smtp.credentials(creds); } let mut transport = smtp.transport(); transport.send(email)?; Ok(()) } ```