cloudroam
2025-02-14 2fce91b8c0faf1290d8a35ee022dab3cdbc28a54
1
2
3
4
5
6
7
8
9
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
package com.example.firstapp.utils.mail
 
import android.text.Html
import android.text.Spanned
import com.example.firstapp.utils.Log
import org.bouncycastle.openpgp.PGPPublicKeyRing
import org.bouncycastle.openpgp.PGPSecretKeyRing
import java.io.File
import java.security.PrivateKey
import java.security.cert.X509Certificate
import java.util.Properties
import javax.mail.Authenticator
import javax.mail.PasswordAuthentication
 
@Suppress("PrivatePropertyName", "DEPRECATION")
class EmailSender(
    // SMTP参数
    private val host: String, // SMTP服务器地址
    private val port: String, // SMTP服务器端口
    private val from: String, // 发件人邮箱
    private val password: String, // 发件人邮箱密码/授权码
    // 邮件参数
    private val nickname: String, // 发件人昵称
    private val subject: String, // 邮件主题
    private val body: CharSequence, // 邮件正文
    private val attachFiles: MutableList<File> = mutableListOf(), // 附件
    // 收件人参数
    private val toAddress: MutableList<String> = mutableListOf(), // 收件人邮箱
    private val ccAddress: MutableList<String> = mutableListOf(), // 抄送者邮箱
    private val bccAddress: MutableList<String> = mutableListOf(), // 密送者邮箱
    // 监听器
    private val listener: EmailTaskListener? = null,
    // 安全选项
    private val openSSL: Boolean = false, //是否开启ssl验证 默认关闭
    private val sslFactory: String = "javax.net.ssl.SSLSocketFactory", //SSL构建类名
    private val startTls: Boolean = false, //是否开启starttls加密方式 默认关闭
    // 邮件加密方式: S/MIME、OpenPGP、Plain(不传证书)
    private val encryptionProtocol: String = "S/MIME",
    // 邮件 S/MIME 加密和签名
    private val recipientX509Cert: X509Certificate? = null, //收件人公钥(用于加密)
    private val senderPrivateKey: PrivateKey? = null, //发件人私玥(用于签名)
    private val senderX509Cert: X509Certificate? = null, //发件人公玥(用于签名)
    //邮件 PGP 加密和签名
    private var recipientPGPPublicKeyRing: PGPPublicKeyRing? = null, // 收件人公钥(用于加密)
    private var senderPGPSecretKeyRing: PGPSecretKeyRing? = null, // 发件人私钥(用于签名)
    private val senderPGPSecretKeyPassword: String = "", // 发件人私钥密码
) {
 
    private val TAG: String = EmailSender::class.java.simpleName
 
    private val properties: Properties = Properties().apply {
        // 设置邮件服务器的主机名
        put("mail.smtp.host", host)
        // 设置邮件服务器的端口号
        put("mail.smtp.port", port)
        // 设置是否需要身份验证
        put("mail.smtp.auth", "true")
        // 设置是否启用 SSL 连接
        if (openSSL) {
            put("mail.smtp.ssl.enable", "true")
            put("mail.smtp.socketFactory.class", sslFactory)
        }
        // 设置是否启用 TLS 连接
        if (startTls) {
            put("mail.smtp.starttls.enable", "true")
        }
    }
 
    suspend fun sendEmail() {
        try {
            val authenticator = MailAuthenticator(from, password)
            // 邮件正文
            val html = try {
                if (body is Spanned) Html.toHtml(body) else body.toString()
            } catch (e: Exception) {
                body.toString()
            }
 
            // 发送 S/MIME 邮件
            when (encryptionProtocol) {
                "S/MIME" -> {
                    val smimeUtils = SmimeUtils(
                        properties,
                        authenticator,
                        from,
                        nickname,
                        subject,
                        html,
                        attachFiles,
                        toAddress,
                        ccAddress,
                        bccAddress,
                        recipientX509Cert,
                        senderPrivateKey,
                        senderX509Cert,
                    )
                    val isEncrypt: Boolean = recipientX509Cert != null
                    val isSign: Boolean = senderX509Cert != null && senderPrivateKey != null
                    Log.d(TAG, "isEncrypt=$isEncrypt, isSign=$isSign")
                    val result = when {
                        isEncrypt && isSign -> smimeUtils.sendSignedAndEncryptedEmail()
                        isEncrypt -> smimeUtils.sendEncryptedEmail()
                        isSign -> smimeUtils.sendSignedEmail()
                        else -> smimeUtils.sendPlainEmail()
                    }
                    listener?.onEmailSent(result.first, result.second)
                }
 
                "OpenPGP" -> {
                    // 发送 PGP 邮件
                    val pgpEmail = PgpUtils(
                        properties,
                        authenticator,
                        from,
                        nickname,
                        subject,
                        html,
                        attachFiles,
                        toAddress,
                        ccAddress,
                        bccAddress,
                        recipientPGPPublicKeyRing,
                        senderPGPSecretKeyRing,
                        senderPGPSecretKeyPassword,
                    )
                    val isEncrypt: Boolean = recipientPGPPublicKeyRing != null
                    val isSign: Boolean = senderPGPSecretKeyRing != null
                    Log.d(TAG, "isEncrypt=$isEncrypt, isSign=$isSign")
                    val result = when {
                        isEncrypt && isSign -> pgpEmail.sendSignedAndEncryptedEmail()
                        isEncrypt -> pgpEmail.sendEncryptedEmail()
                        isSign -> pgpEmail.sendSignedEmail()
                        else -> pgpEmail.sendPlainEmail()
                    }
                    listener?.onEmailSent(result.first, result.second)
                }
 
                else -> {
                    // 发送普通邮件
                    val simpleEmail = SmimeUtils(
                        properties,
                        authenticator,
                        from,
                        nickname,
                        subject,
                        html,
                        attachFiles,
                        toAddress,
                        ccAddress,
                        bccAddress,
                    )
                    val result = simpleEmail.sendPlainEmail()
                    listener?.onEmailSent(result.first, result.second)
                }
            }
        } catch (e: Exception) {
            listener?.onEmailSent(false, "Error sending email: ${e.message}")
        }
    }
 
    interface EmailTaskListener {
        fun onEmailSent(success: Boolean, message: String)
    }
 
    /**
     * 发件箱auth校验
     */
    private class MailAuthenticator(username: String, private var password: String) : Authenticator() {
        private var userName: String? = username
        override fun getPasswordAuthentication(): PasswordAuthentication {
            return PasswordAuthentication(userName, password)
        }
    }
}