仓库源文站点原文

电子邮件的不完整总结

电子邮件 是最早的互联网应用之一。

组成

一个 电子邮件系统 至少由三部分组成

一封 电子邮件 由两个部分组成

一个 电子邮件地址 由三个部分组成

总结一下就是 三种协议,一种格式

<!-- ### 客户端 ### 服务端 ## 三种协议,一种格式 ### SMTP ### IMAP ### POP3 ### Internet Message Format -->

如何搭建一个邮件服务器

单机的在不同用户间发送邮件

收发局域网的邮件

收发外网的邮件

加密

<!-- SMTPS 和 STARTTLS 是两种不一样的加密方式 MTA-STS --> ### SPF、DKIM 和 DMARC ### 垃圾邮件 和 病毒 ## 使用命令行发送邮件 ### 交互式命令 - 适用于 nc telnet openssl - 大致分为五个步骤 - 连接到 smtp 服务器 - nc smtp.qq.com 25 - telnet smtp.qq.com 25 - openssl s_client -connect smtp.qq.com:465 - 建立会话; helo - 身份认证; auth login - 理论上不登录也可以的,但除了本地测试的smtp服务器,不会有不需要登录的 - 账号密码需要转换成 base64 - AUTH LOGIN - 输入 AUTH LOGIN 之后,按提示输入,转换成 base64 的账号密码 - 用户转换账号密码的命令,用 echo 输出时要忽略行尾的空格 ``` echo -n "bootstrap@example.com" | base64 echo -n "SendEmails" | base64 ``` - 例子 ``` AUTH LOGIN Ym9vdHN0cmFwQGV4YW1wbGUuY29t U2VuZEVtYWlscw== ``` - AUTH PLAIN 换行 - AUTH PLAIN 之后,在下一行输入,转换成 base64 的账号密码 - 用户转换账号密码的命令,账号和密码前面都有一个 `\0` ``` printf "\0%s\0%s" "bootstrap@example.com" "SendEmails" | base64 ``` - 例子 ``` AUTH PLAIN AGJvb3RzdHJhcEBleGFtcGxlLmNvbQBTZW5kRW1haWxz ``` - AUTH PLAIN 不换行 - 和 AUTH PLAIN 在同一行输入,转换成 base64 的账号密码 - 用户转换账号密码的命令,账号和密码前面都有一个 `\0`,字符串要以 auth 开头 ``` printf "auth\0%s\0%s" "bootstrap@example.com" "SendEmails" | base64 ``` - 例子 ``` AUTH PLAIN YXV0aABib290c3RyYXBAZXhhbXBsZS5jb20AU2VuZEVtYWlscw== ``` - AUTH LOGIN 大多数 smtp 服务器都支持, AUTH PLAIN 换行 这种方式 smtp.qq.com 就不支持了 - 发送邮件信封(发件人和收件人); MAIL FROM 和 RCPT TO - MAIL FROM 只有一个 - RCPT TO 可以有很多个,包含 header 里的 to cc bcc 这些,只需要写地址就可以了,不用写用户名 - 发送邮件内容(邮件正文和附件); 以 DATA 开始 以 . 结束 - 关闭会话; QUIT - 一个收件人的例子 ``` HELO example.com AUTH LOGIN MAIL FROM:<alice@example.com> RCPT TO:<bob@example.com> DATA Date: Mon, 4 April 2022 From: Alice <alice@example.com> Subject: Eggs benedict casserole To: Bob <bob@example.com> Hi Bob, I will bring the eggs benedict casserole recipe on Friday. -Alice . QUIT ``` - 多个个收件人的例子 ``` HELO example.com AUTH LOGIN MAIL FROM:<alice@example.com> RCPT TO:<bob1@example.com> RCPT TO:<bob2@example.com> DATA Date: Mon, 4 April 2022 From: Alice <alice@example.com> Subject: Eggs benedict casserole To: bob1 <bob1@example.com>, bob2 <bob2@example.com> Hi Bob, I will bring the eggs benedict casserole recipe on Friday. -Alice . QUIT ``` ``` HELO example.com AUTH LOGIN MAIL FROM:<alice@example.com> RCPT TO:<bob1@example.com> RCPT TO:<bob2@example.com> DATA Date: Mon, 4 April 2022 From: Alice <alice@example.com> Subject: Eggs benedict casserole To: bob1 <bob1@example.com> Cc: bob2 <bob2@example.com> Hi Bob, I will bring the eggs benedict casserole recipe on Friday. -Alice . QUIT ``` - smtp 命令 NOOP HELP RSET <!-- 可以用 mailpit 作为测试用的服务器 感觉这个位置应该需要一些截图 还需要继续总结一下有哪些 rfc -->

非交互式命令

<!-- curl 用的是第一种方式,所以当前版本的 curl 无法发送邮件到 smtp.qq.com 加上这个参数 --sasl-ir 1 就能使账号密码放在同一行里了 但加上这个参数后,还会再请求一次,而且还是请求失败的那种,可能是我不了解 sasl curl -v --ssl --url 'smtps://smtp.qq.com:465/smtp.qq.com' \ --user 'alice@example.com:NvbQBTZW5kRW' \ --mail-from 'alice@example.com' \ --mail-rcpt 'bob@example.com' \ --sasl-ir 1 \ --upload-file mail-curl.txt -->

$contentType = New-Object System.Net.Mime.ContentType $contentType.Name = 'v2.php' $contentType.MediaType = System.Net.Mime.MediaTypeNames::Text::Plain System.Net.Mail.Attachment('./v2.php', $contentType)

System.Net.Mime.MediaTypeNames.Text.Plain

System.Net.Mime.MediaTypeNames.ToString() System.Net.Mime.MediaTypeNames.Text

Get-Member -MemberType Property System.Net.Mime.MediaTypeNames | Get-Member

System.Net.Mime.MediaTypeNames.Attributes System.Net.Mime.MediaTypeNames.GetEnumNames

如果是完整的类名,静态类,对象,或者已经赋值的变量 [System.Net.Mail.MailAddress] | Get-Member System.Net.Mime.MediaTypeNames | Get-Member $a | Get-Member

System.Net.Mime.MediaTypeNames | Get-Member $a | Get-Member

[AppDomain]::CurrentDomain.GetAssemblies() | ? {$.Location -and ($.Location.Split('\')[-1] -eq 'System.Net.Mime.MediaTypeNames.dll')}

System.AppDomain::CurrentDomain.GetAssemblies() | ForEach-Object { $_.GetTypes() }

System.AppDomain::CurrentDomain.GetAssemblies() | ForEach-Object { $_.GetTypes() } | Select-Object -First 1 | format-list

System.AppDomain::CurrentDomain.GetAssemblies() | Select-Object -First 1 | format-list System.AppDomain::CurrentDomain.GetAssemblies() | Select-Object -First 1 | Get-Member (System.AppDomain::CurrentDomain.GetAssemblies() | Select-Object -First 1 ).GetName() (System.AppDomain::CurrentDomain.GetAssemblies() | Select-Object -First 1 ).GetTypes()

System.AppDomain::CurrentDomain.GetAssemblies() | ForEach-Object { $.GetTypes() } | Select-Object -First 1 | Get-Member -Name Name Where-Object { $.GetName() -eq "System" } | Get-Member

System.AppDomain::CurrentDomain.GetAssemblies() | ForEach-Object { $.GetTypes() } | ForEach-Object { $.GetMethods(‘NonPublic, Public, Static’) } | ForEach-Object { $MethodInfo = $; $.GetCustomAttributes($false) } | Where-Object { $MethodInfo.Name.ToLower() -eq $FunctionName.ToLower() -and $_.Value -eq $Module } | ForEach-Object { $MethodInfo }

这一段是可行的 只能用在 windows powershell $mscorlib = [AppDomain]::CurrentDomain.GetAssemblies() | ? {$.Location -and ($.Location.Split('\')[-1] -eq 'System.dll')} $Win32Native = $mscorlib.GetType('Microsoft.Win32.NativeMethods') $OpenProcessMethod = $Win32Native.GetMethod('OpenProcess', ([Reflection.BindingFlags] 'NonPublic, Public, Static')) $OpenProcessMethod.Invoke($null, @(0x1F0FFF, $False, 524))

这一段是可行的 只能用在 powershell $mscorlib = [AppDomain]::CurrentDomain.GetAssemblies() | ? {$.Location -and ($.Location.Split('\')[-1] -eq 'System.Diagnostics.Process.dll')} $Win32Native = $mscorlib.GetType('Interop+Kernel32') $OpenProcessMethod = $Win32Native.GetMethod('OpenProcess', ([Reflection.BindingFlags] 'NonPublic, Public, Static')) $OpenProcessMethod.Invoke($null, @(0x1F0FFF, $False, 524))

$mscorlib = [AppDomain]::CurrentDomain.GetAssemblies() | ? {$.Location -and ($.Location.Split('\')[-1] -eq 'System.dll')} | Select-Object -ExpandProperty Namespace -Unique

    $mailMessage.SubjectEncoding = [System.Text.Encoding]::UTF8
    $mailMessage.BodyEncoding = [System.Text.Encoding]::UTF8
    $mailMessage.Subject = "中文Subject"
    $mailMessage.IsBodyHtml = $True
    $mailMessage.Body = "<p>中文mailMessage</p>"
    ```
- System.Web.Mail

printf "auth\0%s\0%s" "12345678@qq.com" "wdgwshjofvvmdeef" | base64

$smtpMail = New-Object System.Web.Mail.SmtpMail

Add-Type -AssemblyName "System.Web" [System.Web.Mail.SmtpMail]

Add-Type -AssemblyName "System.Web" 为什么要单独加载 System.Web 程序集? 因为 powershell 没有默认加载 System.Web

什么是程序集? 就是 .NET 的 .dll 文件 程序集采用可执行文件 (.exe) 或动态链接库文件 (.dll) 的形式,是 .NET 应用程序的构建基块 。 它们向公共语言运行时提供了注意类型实现代码所需的信息。

动态链接会按一定的顺序去搜索 https://learn.microsoft.com/zh-cn/windows/win32/dlls/dynamic-link-library-search-order

Add-Type 也可以直接指定 dll 的路径 Add-Type -Path "C:\path\to\your\MyLibrary.dll"

$smtpClient = New-Object System.Net.Mail.SmtpClient("127.0.0.1", 25)

Get-Help Add-Type

$smtpClient = New-Object System.Net.Mail.SmtpClient("smtp.qq.com", 587) $smtpClient.EnableSsl = $True

$smtpClient = New-Object System.Net.Mail.SmtpClient("127.0.0.1", 25) $smtpClient.Credentials = New-Object System.Net.NetworkCredential("12345678@qq.com", "wdgwshjofvvmdeef") $mailMessage = New-Object System.Net.Mail.MailMessage $mailMessage.From = New-Object System.Net.Mail.MailAddress("jane@contoso.com", "Jane Clayton") $mailMessage.To.Add($(New-Object System.Net.Mail.MailAddress("jane@contoso2.com", "Jane Clayton2"))) $mailMessage.To.Add($(New-Object System.Net.Mail.MailAddress("jane@contoso3.com", "Jane Clayton3"))) $mailMessage.CC.Add($(New-Object System.Net.Mail.MailAddress("jane@contoso4.com", "Jane Clayton4"))) $mailMessage.CC.Add($(New-Object System.Net.Mail.MailAddress("jane@contoso5.com", "Jane Clayton5"))) $mailMessage.SubjectEncoding = [System.Text.Encoding]::UTF8 $mailMessage.BodyEncoding = [System.Text.Encoding]::UTF8 $mailMessage.Subject = "中文Subject" $mailMessage.IsBodyHtml = $True $mailMessage.Body = "<p>中文mailMessage</p>"

$winDir = New-Object -TypeName "System.Text.Encoding"

New-Object System.Text.Encoding

$smtpClient.Send($mailMessage);

IsBodyHtml

$smtpClient.Send("12345678@qq.com", "87654321@qq.com", "Subject", "mailMessage");

.NET 中的程序集就是 exe 或 dll 程序集通常有一个或多个命名空间

程序集需要先加载再使用 通常会默认加载一部分程序集

jsc csc vbc

windows powershell 依赖 .NET Framework 所以系统里一定有 .NET Framework 有 .NET Framework 也会有 csc jsc vbc 这三个编译器

powershell core 之后有自包含 .NET Framework 的安装包,所以 powershell 可以不依赖 .NET Framework

PowerShell 中只要变量不为 null ,就可以用 Get-Member 方法

在 PowerShell 中,可以通过几种方法判断一个变量是否为 null。以下是一些常用的方法:

1. 使用 -eq 运算符

你可以直接将变量与 $null 进行比较:

if ($variable -eq $null) {
    Write-Host "变量是 null"
} else {
    Write-Host "变量不是 null"
}

2. 使用 -ne 运算符

如果你想检查变量是否不为 null,可以使用 -ne 运算符:

if ($variable -ne $null) {
    Write-Host "变量不是 null"
} else {
    Write-Host "变量是 null"
}

3. 简单的 if 语句

你还可以使用简单的 if 语句来检查变量的存在性:

if ($variable) {
    Write-Host "变量不是 null 或空"
} else {
    Write-Host "变量是 null 或空"
}

4. 使用 IsNullOrEmpty 方法

如果你想同时检查变量是否为 null 或空字符串,可以使用 IsNullOrEmpty 方法:

if ([string]::IsNullOrEmpty($variable)) {
    Write-Host "变量是 null 或空字符串"
} else {
    Write-Host "变量不是 null 且不是空字符串"
}

总结

这些方法可以帮助你有效地判断 PowerShell 中的变量是否为 null。选择适合你需求的方法即可。如果你有其他问题或需要进一步的帮助,请告诉我!

-->

php 发送邮件

使用原生的函数

使用第三方库

如何在邮件里显示图片

名词

roundcube-framework-1.6.10.tar.gz roundcubemail-1.6.10-complete.tar.gz roundcubemail-1.6.10.tar.gz <!--

docker run --name emailsystem --rm -it -p 8080:8080 php:8.1-bullseye /bin/bash

cp /etc/apt/sources.list /etc/apt/sources.list_bak && \ sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list && \ apt update && \ apt install -y vim && \ apt install -y curl && \ apt install -y net-tools && \ apt install -y netcat && \ apt install -y procps && \ apt install -y dnsutils && \ apt install -y mailutils && \ apt install -y postfix && \ apt install -y bzip2 && \ apt install -y make && \ apt install -y gcc && \ apt install -y git && \ cd && \ curl -O "https://busybox.net/downloads/busybox-1.36.1.tar.bz2" && \ tar -xjf ./busybox-1.36.1.tar.bz2 && \ cd busybox-1.36.1 && \ make defconfig && \ make install && \ cp /root/busybox-1.36.1/busybox /bin/busybox && \ cd

curl -sS -o composer.phar https://getcomposer.org/composer.phar && \ chmod 0755 composer.phar && \ mv composer.phar /usr/local/bin/composer

cp /usr/local/etc/php/php.ini-development /usr/local/etc/php/php.ini

apt install -y libzip-dev unzip && \ docker-php-ext-install zip

apt install -y libicu-dev && \ docker-php-ext-install intl

docker-php-ext-install exif

apt install -y \ libfreetype6-dev \ libjpeg62-turbo-dev \ libjpeg-dev \ libpng-dev \ libwebp-dev && \ docker-php-ext-configure gd --with-jpeg --with-freetype && \ docker-php-ext-install gd

docker-php-ext-install opcache

curl -L -o roundcubemail.tar.gz --connect-timeout 120 --max-time 3600 --retry 100 --retry-delay 5 https://github.com/roundcube/roundcubemail/releases/download/1.6.10/roundcubemail-1.6.10-complete.tar.gz

tar -zxf roundcubemail.tar.gz

cd roundcubemail-1.6.10

cp composer.json composer.json-bak cp composer.json-dist composer.json

composer update

PHP_CLI_SERVER_WORKERS="10" php -S 127.0.0.1:8080 PHP_CLI_SERVER_WORKERS="10" php -S 0.0.0.0:8080

https://8080-cs-285386365050-default.cs-asia-east1-jnrc.cloudshell.dev/installer/index.php

touch roundcubemail.sqlite

cat config/config.inc.php vi config/config.inc.php

sqlite:///roundcubemail.sqlite

chmod 777 roundcubemail.sqlite

-->

<!-- 电子邮件 名词 email e-mail Electronic Mail 电子的 邮件 电子邮件 电邮 组成 mta mua mda 主流的操作系统都有预装邮件客户端(mua) Microsoft Mail 更早的,从dos时代开始 Windows Messaging 95 , win95里的 ie3.0 也可以作为mua,这个mua后续发展出 Outlook Express Outlook Express 98-xp Windows Live Mail vista-7 Windows Mail 8-11 Outlook For Windows 10-11 unix/linux 里的 mailx iOS 安卓 winphone 塞班 S40 都有预装邮件客户端,就像浏览器一样是一个重要的系统组件 如何搭建一个邮件服务器 单机的在不同用户间发送邮件 apt install -y mailutils 这句命令安装的是 exim 如无意外,安装完后就能直接用 sendmail mail mailx 命令了 mail 和 mailx 链接到 mail.mailutils sendmail 链接到 exim4 增加一个用户 useradd -m user1 发送邮件给其它用户 使用 mailx 命令发送 echo "test mail content" | mailx -s "test mail subject" root echo 邮件内容 | mailx -s 邮件主题 用户名 使用 sendmail 命令发送 echo -e "From: root\nTo: root\nSubject: 问候\n\n我是 root 用户,这是一封用 sendmail 命令发送的邮件。" | sendmail -t sendmail -t <<EOF From: root To: root Subject: 问候 我是 root 用户,这是一封用 sendmail 命令发送的邮件。 EOF; 查看用户的邮件 mail -u root mail -u 用户名 mail -f /var/spool/mail/mail mail -f /var/mail/mail root用户要用这种方式才能查看接收的邮件 mail -f 用于保存邮件的文件路径 mail -f /var/mail/user1 邮件保存的位置 目录 /var/spool/mail 这个目录会软连接到 /var/mail /var/mail 这个是实际的目录 文件 /var/mail/root /var/mail/mail /var/mail/用户名 日志 日志文件的目录 /var/log/exim4 日志文件 /var/log/exim4/mainlog 预防万一可以先事先新建一个 ls -l /var/log/exim4 mkdir -p /var/log/exim4 touch /var/log/exim4/mainlog chmod 777 /var/log/exim4/mainlog 查看日志 cat /var/log/exim4/mainlog 在局域网里自娱自乐 至少需要搭建 smtp 和 imap 至少要开启这两个端口 smtp(25) imap(143) 最好再加上这两个 smtps(587) imaps(993) 还有这几个 smtps(465) pop3(110) pop3s(995) smtp(2525) 587 才是标准的 smtps 端口 rfc3207 465 比 587 更早出现 465 是一开连接就启用 tls 465 很长一段时间都不是标准,直到 rfc8314 587 是连接完成后再通过 STARTTLS命令 启用 tls 除了 smtps 之外,还有不少的协议使用这种明文命令的形式启用 tls 这种 tls 被称为 机会型TLS(Opportunistic TLS) 有些电子邮件服务通过端口 2525 提供 SMTP 传输。但它并不是电子邮件的标准端口 假设已经安装好 exim 修改配置文件 启动 尝试通过ip发送邮件 能收发外网的邮件 除了 搭建 smtp 和 imap 之外,还要做好域名的解释 域名解释才是最困难的部分 域名解释 A 记录 @ 指向 ip CNAME 记录 mail 指向 A 记录 MX 记录 @ 指向 CNAME 记录 TXT 记录 spf dkim dmarc DANE MTA-STS PTR 记录 域名 指向 ip 各种代理 mua webmail RainLoop mta exim Postfix sendmail amavisd-new 调度 ClamAV 和 SpamAssassin ClamAV 邮件反病毒 SpamAssassin 过滤垃圾邮件 mda Dovecot msa mra maa opendkim 还有更多? bimi RUA RUF MTA-STS TLS-RPT 要确保这几个端口的开放 25 465 143 993 除了安全组,防火墙,还要向运营商确认这几个端口有没有开放 mta Mail Transfer Agent(邮件传输代理) 这个是邮件系统的核心 主要用于 接收邮件 把邮件转发给其它mta 把邮件转发给mda或保存在本地 常见的 mta 有三种 sendmail 最古老的 mta 之一 可以通过命令行发送邮件 可以作为 smtp 服务器运行 postfix Postfix 是为了替代 Sendmail 而设计的,提供了更现代化的特性和更高的安全性 兼容一部分 sendmail 的命令 exim4 兼容一部分 sendmail 的命令 -->