泥嚎呀,今天想聊一聊邮件的事。
我们平常可能会收到来自各种网站的邮件,以我订阅的 WakaTime 为例,它会给我推送一个周报:
💡 WakaTime 可以和主流 IDE 或者编辑器关联,统计 coding 的时长等数据。我这里用的邮箱客户端是 qq 邮箱网页版,我们可以很容易看出这里是一个 HTML 文本,可以用 devtools 查看一下内容区域的结构是什么样的:
它的主体结构是下面这样的:
1 | <div class="readmail_content"> <div class="readmail_content_html qmbox qmbox_Web" id="readmail_content_html"> <style>style> <div bgcolor="#F7F7F7">div> div>div> |
显然这就是邮件的主体结构,最外层的两个 div 元素是 qq 邮箱添加的,而我们看到的 html 文本则是被放入了里面。
💡 这个结构在不同邮箱网站中会不一样,请自行探索🕶。既然如此,我们能否用编程的方式,发送一个被华丽的 CSS 装饰的 HTML 文本给别人呢?当然可以😎,这就是接下来要尝试的。
nodemailer 启动!
用我熟悉的 Node 可以快速搭建一个发送邮件的服务,用到的库是 nodemailer
首先,我们需要添加 nodemailer 这个包:
1 | npm install nodemailer |
接下来在项目中导入:
1 | // index.jsconst nodemailer = require('nodemailer'); |
既然要发送邮件,我们肯定需要一个账号,可以用 nodemailer.createTransport 定义一个发送邮件的对象,并指定账号和密码:
1 | const transporter = nodemailer.createTransport(,}); |
我来解释一下关键参数的含义:
- host : host 是指邮件服务器的主机地址。对于 qq 邮箱,SMTP 服务器的主机地址是 smtp.qq.com。SMTP(Simple Mail Transfer Protocol,简单邮件传输协议)是用于发送电子邮件的互联网标准协议。每个电子邮件服务提供商都会有自己独特的 SMTP 服务器地址。例如,Gmail 的 SMTP 服务器地址是 smtp.gmail.com。
💡 可以在邮箱的帮助文档中查询到对应的信息1. port : 一般是 456 或者 587, 这两个都是加密端口 - auth : 这里的 pass 不是密码,而是用授权码,授权码可以从邮箱的客户端中申请获得。
完成之后我们只需要调用 transporter.sendMail 发送邮件即可,这里我写了一个异步函数来完成这件事:
1 | const content = ` |
`async function sendMail() ) console.log(“Message sent: %s”, info.messageId);}sendMail().catch(console.error);这里的 content 是一个加载动画,具体实现比较复杂,实现的方式不是重点 :>
这是在不同设备上面看到的效果:
- 安卓 qq 邮箱 APP:
- qq 邮箱网页版:
App 中能看到正常的 CSS 动画,但是在网页端我们什么都没看到,这是为什么?
content 被篡改
打开 devtools, 观察一下我们的 content 部分的 html:
1 | <div class="readmail_content_html qmbox qmbox_Web" id="readmail_content_html"> <style> .qmbox .loader_OZAT .qmbox .loader::before .qmbox .loader::after @keyframes l10-0 .qmbox 50%,.qmbox 100% } @keyframes l10-1 } @keyframes l10-2 .qmbox 80%,.qmbox 100% } style> <div class="loader_OZAT">div>div> |
可以观察到我们的 content 被 qq 邮箱篡改了!
具体来说:
- 每个选择器前面被加上了 .qmbox 这个类选择器。
- @keyframes 内部被错误地添加上了 .qmbox ,造成语法错误
- div 的 loader 类变成了 loader_OZAT
第一条是比较好理解的,通过加上前缀,来防止类名污染。
但是 2, 3 条的机制我就不是很能理解了,尤其是 2 显然是一个 bug,这直接导致动画失效。
不过我们能推断出 qq 邮箱是会对发来的 html 做处理的,来保证网站和用户的安全。
复原之后我们的马里奥又回来辣!
## js 是否能插入到 content 里?
那既然我们能在 content 里面写 html 和 css,那么我们能否写 js 呢?
考虑到安全,qq 邮箱应该是会删掉 content 里面的 js 的,我们可以来验证一下。
把 content 替换为下面的内容:
1 | const content = ` 计数器: 0 |