邮件协议汇总

概述

在我们日常开发过程中,经常会涉及到邮件的发送、接收。一方面,邮件服务相比于短信服务会更便宜,其对接方式也更方便,不像短信的发送,需要很多东西进行审核。

在对邮件开发前,我们必须对邮件协议有一个整体了解,如使用场景、特性等。

常用的邮件协议

  • 简单邮件传输协议(SMTP)
  • 多用途因特网邮件扩展(MIME)
  • 邮局协议(POP3/POP)
  • 互联网消息访问协议(IMAP/IMAP4)

邮件系用组成

一个邮件系统通常有四部分组成,邮件用户代理 (MUA)、邮件传递代理 (MDA)、邮件传输代理 (MTA)以及邮件检索代理(MRA)。

MUA就是邮件客户端,常用的有outlook、thunderbird、Mac Mail、网易邮件大师等软件。邮件客户端使用IMAP或POP3协议与服务器通信,Client端用户都需要通过各个操作系统提供的MUA才能够使用邮件系统。比如,Windows里的OutLook Express、Netscape里的mail功能与KDE里的Kmail都是MUA。MUA主要的功能就是接收邮件主机的电子邮件,并提供用户浏览与编写邮件的功能。

MTA是用在邮件主机上的软件,它也是主要的邮件服务器。MTA负责发送邮件,中转邮件,当然也要接收邮件。常见的有sendmail、postfix。

MDA主要的功能就是将MTA接收的信件依照信件的流向(送到哪里)将该信件放置到本机账户下的邮件文件中(收件箱),或者再经由MTA将信件送到下个MTA。如果信件的流向是到本机,这个邮件代理的功能就不只是将由MTA传来的邮件放置到每个用户的收件箱,它还可以具有邮件过滤(filtering)与其他相关功能。常见的有procmail、dropmail;

MRA负责实现IMAP与POP3协议,与MUA进行交互;相当于让你的邮件账户支持离线邮件收取,而不是电脑打开才能收取邮件,常见的有dovecot。

邮件发送流程

  1. 用户利用MUA寄信到MTA。配置MUA时要配上smtp服务器域名,然后发送邮件。比如使用网易账户发送邮件就要在配置smtp.163.com,这样邮件就发送到网易MTA。
  2. MTA检查收件人服务器如果不是自己则传递到下一跳MTA,直到传递到目的MTA。
  3. 目的MTA收到邮件后将邮件存储到MDA中,MDA对邮件进行垃圾过滤,病毒查杀。
  4. MRA把邮件从MDA收取到用户的收件箱中。
  5. MUA收取邮件,此时收取邮件只是将MRA中的邮件下载到本地。

协议

邮件组成部分

一封邮件包括信封、邮件头和邮件体等三个部分。

信封

信封可以简单理解为邮件载体,即用来承载邮件头和邮件体的一个逻辑结构。

邮件头

邮件头分邮件头域、Content-Type域和Content-Transfer-Encoding域三部分。其字段含义如下

字段 含义 添加者
Received 传输路径 各级邮件服务器
Return-Path 回复地址 目标邮件服务器
Delivered-To 发送地址 目标邮件服务器
Reply-To 回复地址 邮件的创建者
From 发件人地址 邮件的创建者
To 收件人地址 邮件的创建者
Cc 抄送地址 邮件的创建者
Bcc 暗送地址 邮件的创建者
Date 日期和时间 邮件的创建者
Subject 主题 邮件的创建者
Message-ID 消息ID 邮件的创建者
MIME-Version MIME版本 邮件的创建者
Content-Type 内容的类型 邮件的创建者
Content-Transfer-Encoding 内容的传输编码方式 邮件的创建者
邮件头域

邮件头包含了发件人、收件人、主题、时间、MIME版本、邮件内容的类型等重要信息。每条信息称为一个域,由域名后加“: ”和信息内容构成,可以是一行,较长的也可以占用多行。域的**首行必须“顶头”**写,即左边不能有空白字符(空格和制表符);续行必须以空白字符打头,且第一个空白字符不是信息本身固有的,解码时要过滤掉。

非标准的、自定义域名都以X-开头,例如X-Mailer, X-MSMail-Priority等,通常在接收和发送邮件的是同一程序时才能理解它们的意义。

Content-Type域

Content-Type域,即内容类型域,它用来说明传输的内容的类型。Cotent-Type域又由“主类型/子类型”构成,主类型有text, image, audio, video, application, multipart, message等,分别表示文本、图片、音频、视频、应用、分段、消息等。每个主类型都可能有多个子类型,如text类型就包含plain, html, xml, css等子类型。以X-开头的主类型和子类型,同样表示自定义的类型,未向IANA正式注册,但大多已经约定成俗了。如application/x-zip-compressed是ZIP文件类型。在Windows中,注册表的“HKEY_CLASSES_ROOT/MIME/Database/Content Type”内列举了除multipart之外大部分已知的Content-Type。各种类型一般都可以带参数。至于参数的形式,RFC里有很多补充规定,有的允许带几个参数,常见类型如下

类型 属性名 含义
text charset 用于说明文本内容的字符集编码
image name 用于说明图片文件的文件名
application name 用于说明应用程序的文件名
multipart boundary 用于定义MIME消息之间的分隔符
Content-Transfer-Encoding域

Content-Transfer-Encoding域即传送编码域,它用来说明后面传输的内容的编码方式。

Content-Transfer-Encoding共有Base64, Quoted-printable, 7bit, 8bit, Binary等几种。其中7bit是缺省的编码方式。电子邮件源码最初设计为全部是可打印的ASCII码的形式。非ASCII码的文本或数据要编码成要求的格式,如上面的三个例子。Base64, Quoted-Printable是在非英语国家使用最广使的编码方式。Binary方式只具有象征意义,而没有任何实用价值。关于Base64编码和Quoted-Printable编码请参考RFC文档或另外一篇文章《SMTP协议分析》。国内多数邮件服务器已经支持8bit方式,因此只在国内传输的邮件,特别是在邮件头中,可直接使用8bit编码,对汉字不做处理。如果邮件要出国,建议按Base64或Quoted-printable编码。

邮件体

邮件体的类型由邮件头的“Content-Type”域指出。常见的简单类型有text/plain(纯文本)text/html(超文本)。源码中出现的multipart类型,是MIME邮件的精髓。邮件体被分为多个段,每个段又包含段头和段体两部分,这两部分之间也以空行分隔。常见的multipart类型有三种:multipart/mixed, multipart/related和multipart/alternative。从它们的名称,不难推知这些类型各自的含义和用处。


邮件协议

SMTP协议
SMTP协议简介

SMTP称为简单邮件传输协议(Simple Mail Transfer Protocal),目标是向用户提供高效、可靠的邮件传输。它的一个重要特点是它能够在传送中接力传送邮件,即邮件可以通过不同网络上的主机接力式传送。通常它工作在两种情况下:一是邮件从客户机传输到服务器;二是从某一个服务器传输到另一个服务器。SMTP是一个请求/响应协议,它监听25号端口,用于接收用户的Mail请求,并与远端Mail服务器建立SMTP连接。

SMTP工作机制

SMTP通常有发送和接收两种工作模式。发送SMTP在接收到用户的邮件请求后,判断此邮件是否为本地邮件,若是直接投送到用户的邮箱,否则向DNS查询远端邮件服务器的MX记录,并建立与远端接收SMTP之间的一个双向传送通道,此后SMTP命令由发送SMTP发出,由接收SMTP接收,而应答则反方向传送。

一旦传送通道建立,SMTP发送者发送MAIL命令指明邮件发送者。如果SMTP接收者可以接收邮件则返回OK应答。SMTP发送者再发出RCPT命令确认邮件是否接收到。如果SMTP接收者接收,则返回OK应答;如果不能接收到,则发出拒绝接收应答(但不中止整个邮件操作),双方将如此反复多次。当接收者收到全部邮件后会接收到特别的序列,入伏哦接收者成功处理了邮件,则返回OK应答。

SMTP交互流程
  1. 建立TCP连接
  2. 客户端发送HELO命令以标识发件人自己的身份,然后客户端发送MAIL命令;服务器端正希望以OK作为响应,表明准备接收。
  3. 客户端发送RCPT命令,以标识该电子邮件的计划接收人,可以有多个RCPT行;服务器端则表示是否愿意为收件人接收邮件。
  4. 协商结束,发送邮件,用命令DATA发送。
  5. 以.表示结束输入内容一起发送出去。
  6. 结束此次发送,用QUIT命令退出

发送示例:

C: telent SMTP.163.com 25  //以telenet方式连接163邮件服务器
S: 220 163.com Anti-spam GT for Coremail System //220为响应数字,其后的为欢迎信息
C: HELO SMTP.163.com //除了HELO所具有的功能外,EHLO主要用来查询服务器支持的扩充功能 
S: 250-mail
S: 250-AUTH LOGIN PLAIN
S: 250-AUTH=LOGIN PLAIN
S: 250 8BITMIME //最后一个响应数字应答码之后跟的是一个空格,而不是'-' 
C: AUTH LOGIN   //请求认证
S: 334 dxNlcm5hbWU6  //服务器的响应——经过base64编码了的“Username”=
C: Y29zdGFAYW1heGl0Lm5ldA==  //发送经过BASE64编码了的用户名
S: 334 UGFzc3dvcmQ6  //经过BASE64编码了的"Password:"=
C: MTk4MjIxNA==  //客户端发送的经过BASE64编码了的密码
S: 235 auth successfully  //认证成功 
C: MAIL FROM: [email protected]  //发送者邮箱
S: 250.  //“…”代表省略了一些可读信息
C: RCPT TO: [email protected] //接收者邮箱
S: 250.    // “…”代表省略了一些可读信息
C: DATA //请求发送数据
S: 354 Enter mail, end with "." on a line by itself
C: Enjoy Protocol Studing
C: .
S: 250 Message sent
C: QUIT //退出连接 
S: 221 Bye
MIME协议
MIME协议简介

MIME全称Multipurpose Internet Mail Extensions,即多用途网际邮件扩充协议。MIME试图在不改变SMTP协议和RFC822(邮件格式标准)的基础上,使得邮件可以传送任意二进制文件。所以MIME 的格式灵活。MIME 消息可以包含文本、图象、声音、视频及其它应用程序的特定数据。具体来说,MIME 允许邮件包括如下格式:

  • 单个消息中可含多个对象
  • 文本文档不限制一行长度或全文长度
  • 可传输 ASCII 以外的字符集,允许非英语语种的消息
  • 多字体消息
  • 二进制或特定应用程序文件
  • 图象、声音、视频及多媒体消息
MIME协议改进点

一封邮件包括信封、邮件头和邮件体等三个部分。信封显然可以不含有二进制信息,而其它两部分则可能包含任意二进制序列,因此需要加以改进。MIME正是抓住了这两个地方来对他们加以改进。

  1.  新增了一些邮件头信息,用来协商MIME的一些参数。 
    
  2.  定义了许多邮件内容的格式,对多媒体电子邮件的表示方法进行了标准化。 
    
  3.  定义了传送编码,从而可以传送任意二进制文件。 
    

我们可以把这些改进措施,看成是在用SMTP等发送邮件前所采取的预处理。

MIME协议结构

一封MIME 邮件可以由多个不同类型的MIME消息组合而成,一个MIME消息表示邮件中的一个基本MIME资源或若干基本MIME消息的组合体。每个MIME消息的 数据格式与RFC822数据格式相似,也包括头和体两部分,分别称为MIME消息头和MIME消息体,它们之间使用空行分隔。MIME消息体中包含了资源的具体内容,MIME消息头中则包含了对资源的描述信息。多个消息,它们共同作为所形成的MIME组合消 息的MIME消息体,相互之间采用某种分隔标识符进行分隔,MIME组合消息的消息头中需要描述其中的多个MIME消息的组合类型和分隔标识符。一个 MIME组合消息还可以再与其他MIME消息共同形成一个更大的MIME组合消息,这样就形成了一种多层嵌套的组合关系,一封MIME邮件就是按这种组合 方式所形成的一个最顶层的MIME组合消息。

MIME消息的头字段
头字段 含义
Content-Type 段体的类型
Content-Transfer-Encoding 段体的传输编码方式
Content-Disposition 段体的安排方式
Content-ID 段体的ID(cid来源)
Content-Location 段体的位置(路径)
Content-Base 段体的基位置
  1. Content-Type

    Content-Type是SMTP协议里原本就有的字段,MIME协议对其进行了扩充。对于MIME组合消息,它的消息头中需要指定组合关系。具体资源的数据类型和 组合消息的组合关系,都是通过消息头中的Content-Type头字段来指定的。Content-Type字段中的内容以“主类型/子类型”的形式出 现,主类型有 text、image、audio、video、application、multipart、message等,分别表示文本、图片、音频、视频、应用 程序、组合结构、消息等。每个主类型下面都有多个子类型,例如text主类型包含plain、html、xml、css等子类型。multipart主类 型用于表示MIME组合消息,它是MIME协议中最重要的一种类型。一封MIME邮件中的MIME消息可以有三种组合关系:混合、关联、选择,它们对应 MIME类型如下:

    • 混和组合类型(multipart/mixed)

      其内容可以是文本、声音和附件等不同邮件内容的混和体,如整封邮件的MIME类型就定义为multipart/mixed

    • 关联(依赖)组合类型(multipart/related)

      如邮件正文要使用HTML代码引用内嵌的图片资源,它们组合成的MIME消息的MIME类型就应 该定义为multipart/related,表示其中某些资源(HTML代码)要引用(依赖)另外的资源(图像数据),引用资源与被引用的资源必须组合成multipart/related类型的MIME组合消息。

    • 选择组合类型( multipart/alternative)

      如一封邮件的邮件正文同时采用HTML格式和普通文本格式进行表达时,就可以将它们嵌套在一个 multipart/alternative类型的MIME组合消息中。这种做法的好处在于如果邮件阅读程序不支持HTML格式时,可以采用其中的文本格 式进行替代

    要在邮件中要添加附件,就必须将整封邮件的MIME类型定义为multipart/mixed;如果要在HTML格式的正文中引用内嵌资 源,那就要定义multipart/related类型的MIME消息;如果普通文本内容与HTML文本内容共存,那就要定义multipart /alternative类型的MIME消息。如果整封邮件中只有普通文本内容与HTML文本内容,那么整封邮件的MIME类型则应定义为multipart/ alternative;如果整封邮件中包含有HTML文本内容和内嵌资源,但不包含附件,那么整封邮件的MIME类型则应定义为 multipart/related。

  2. Content-Transfer-Encoding

    Content-Transfer-Encoding 头字段用于指定MIME消息体中的内容所采用的邮件编码方式

    传统的 SMTP协议又是基于ASCII码字符设计的,基于传统SMTP协议设计的SMTP服务器在处理邮件内容时只取出每个字节中的7个低bit位进行处理,而将最高bit位忽略不计。为了能够在邮 件内容中包含中文、图像或声音等非ASCII字符的数据,可以采用某种编码方式将非ASCII字符的数据转换成可打印的ASCII字符后再发送。邮件阅读程序则按照相应的解码方式从邮件中还原出原始数据即可,比较常用的两种邮件编码方式为BASE64和Quoted-printable。扩展 SMTP协议允许直接在邮件中传递二进制数据,这种没有进行邮件编码的二进制数据的邮件内容称为8bit编码。为了区别,将没有进行邮件编码的纯ASCII码字符的邮件称为7bit编码

    编码方式如下:

    • 7Bit

      消息体内容全部是没有经过编码的ASCII字符。

    • 8Bit

      消息体内容是没有经过编码的原始数据,且其中包含有非ASCII字符的数据。现在的邮件服务器基本上都支持8Bit编码,使用支持8Bit编码的邮件服务器可以简化邮件的处理过程。

    • BASE64

      将二进制数据转换成可打印的ASCII字符的一种最常见的编码方式,它的基本原理是将一组连续的字节数据按6个bit位进行分组,然后对每组数据用一个ASCII字符来表示。6个bit位最多能表示26=64个数值,因此可以使用64个ASCII字符来对应这64个数值,这64个ASCII字符为:

      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
      

      编码细节请参阅其他资料,另外MIME规范建议BASE64编码结果中的每行最多76个字符。

    • Quoted-printable

      Quoted- printable也是一种将二进制数据转换成可打印的ASCII字符的编码方式,它对ASCII字符不进行转换,只对非ASCII字符的数据进行编码转化。每个非ASCII字符的字节数据,都被转换成一个"="号后跟这个字节的十六进制数据。

  3. Content-Disposition

    Content- Disposition头字段用于指定邮件阅读程序处理数据内容的方式,有inlineattachment两种标准方式,inline表示直接处理, 而attachment表示当做附件处理。如果将Content-Disposition设置为attachment,在其后还可以指定filename 属性,如下面例子:

    Content-Disposition: attachment; filename="1.bmp"
    

    表示MIME消息体的内容为邮件附件,附件名"1.bmp"。

  4. Content-ID (内部资源引用)

    Content- ID头字段用于为“multipart/related”组合消息中的内嵌资源指定一个唯一标识号,在HTML格式的正文中可以使用这个唯一标识号来引用 该内嵌资源。如假设将一个表示内嵌图片的MIME消息的Content-ID头字段设置为下述样式:

    Content-ID: it315logo_gif
    

    则在HTML正文中通过如下HTML语句来引用该图片资源

    <img src="cid:it315logo_gif">
    

    注意,在引用Content-ID头字段标识的内嵌资源时,要在资源的唯一标识号前面加上 cid:,以说明要采用唯一标识号对资源进行引用。

  5. Content-Location (外部资源引用)

    Content- Location头字段用于为内嵌资源设置一个URI地址,这个URI地址可以是绝对或相对的。当使用Content- Location头字段为一个内嵌资源指定一个URI地址后,在HTML格式的正文中也可以使用这个URI来引用该内嵌资源。例如,假设将一个表示内嵌图 片的MIME消息的Content-Location头字段设置为如下形式:

    Content-Location:http://www.it315.org/images/it315logo.gif
    

    则在HTML正文中就可以使用如下HTML语句来引用该图片资源:

    
    
  6. Content-Base

    Content- Base头字段用于为内嵌资源设置一个基准路径,只有这样,Content-Location头字段中设置的URI才可以采用相对地址。假如将一个 表示内嵌图片的MIME消息的Content-Base和Content-Location头字段设置为如下形式:

    Content-Base: http://www.it315.org/images/
    Content-Location: it315logo.gif 
    

    则内嵌资源的完整路径就是Content-Base头字段设置的基准路径与Content-Location头字段设置的相对路径相加的结果,在HTML正文中就可以使用如下HTML语句来引用该图片资源:

    <img src="http://www.it315.org/images/it315logo.gif"/>
    
POP/POP3协议
协议简介

POP协议是第一个离线邮件协议,允许用户从服务器上把邮件下载到本地主机上,同时删除保存在邮件服务器上的邮件(现在的邮件服务商都可以设置是否同时删除),能让用户不必长时间地与邮件服务器连接,从而减少了服务器和网络的整体开销;邮局协议版本3 POP3(Post Office Protocol-Version 3)由RFC1939 定义,主要用于支持使用客户端远程管理在服务器上的电子邮件。默认端口号是110。POP3协议相对简单,POP3服务器响应一般为:“+OK”表示成功,“-ERR”表示失败。

收取邮件过程
  1. 用户运行用户代理(如Foxmail, Outlook Express)
  2. 用户代理(以下简称客户端)与邮件服务器(以下简称服务器端)的110端口建立TCP连接
  3. 客户端向服务器端发出各种命令,来请求各种服务(如查询邮箱信息,下载某封邮件等)
  4. 服务端解析用户的命令,做出相应动作并返回给客户端一个响应
  5. 3)和4)交替进行,直到接收完所有邮件转到步骤6),或两者的连接被意外中断而直接退出
  6. 用户代理解析从服务器端获得的邮件,以适当地形式(如可读)的形式呈现给用户
命令格式
POP3命令格式 说明
userusername user 命令是POP3客户端程序与POP3邮件服务器建立连接后通常发送的第一条命令,参数 username 表示收件人的帐户名称。
passpassword pass 命令是在user命令成功通过后,POP3客户端程序接着发送的命令,它用于传递帐户的密码,参数 password 表示帐户的密码。
apopname,digest apop 命令用于替代user和pass命令,它以MD5 数字摘要的形式向POP3邮件服务器提交帐户密码。
stat stat 命令用于查询邮箱中的统计信息,例如:邮箱中的邮件数量和邮件占用的字节大小等。
uidlmsg# uidl 命令用于查询某封邮件的唯一标志符,参数msg#表示邮件的序号,是一个从1开始编号的数字。
list[MSG#] list 命令用于列出邮箱中的邮件信息,参数 msg#是一个可选参数,表示邮件的序号。当不指定参数时,POP3服务器列出邮箱中所有的邮件信息;当指定参数msg#时,POP3服务器只返回序号对应的邮件信息。
retrmsg# retr 命令用于获取某封邮件的内容,参数 msg#表示邮件的序号。
delemsg# dele 命令用于在某封邮件上设置删除标记,参数msg#表示邮件的序号。POP3服务器执行dele命令时,只是为邮件设置了删除标记,并没有真正把邮件删除掉,只有POP3客户端发出quit命令后,POP3服务器才会真正删除所有设置了删除标记的邮件。
rest rest 命令用于清除所有邮件的删除标记。
topmsg#n top 命令用于获取某封邮件的邮件头和邮件体中的前n行内容,参数msg#表示邮件的序号,参数n表示要返回邮件的前几行内容。使用这条命令以提高 Web Mail系统(通过Web站点上收发邮件)中的邮件列表显示的处理效率,因为这种情况下不需要获取每封邮件的完整内容,而是仅仅需要获取每封邮件的邮件头信息。
noop noop 命令用于检测POP3客户端与POP3服务器的连接情况。
quit quit 命令表示要结束邮件接收过程,POP3服务器接收到此命令后,将删除所有设置了删除标记的邮件,并关闭与POP3客户端程序的网络连接。
协议状态

POP3协议中有三种状态,认证状态,处理状态,和更新状态。命令的执行可以改变协议的状态,而对于具体的某命令,它只能在具体的某状态下使用。客户机与服务器刚与服务器建立连接时,它的状态为认证状态;一旦客户机提供了自己身份并被成功地确认,即由认可状态转入处理状态; 在完成相应的操作后客户机发出QUIT命令(具体说明见后续内容),则进入更新状态,更新之后又重返认证状态;当然在认可状态下执行QUIT命令,可释放连接。

协议缺陷

当用户接收电子邮件时,所有的信件都从服务器上清除并下载到客户机。在整个收信过程中,用户无法知道邮件的具体信息,只有照单全收入硬盘后,才能慢慢浏览和删除。这使用户几乎没有对邮件接收的控制决定权。一旦碰上邮箱被轰炸,或有比较大的邮件,用户不能通过分析邮件的内容及发信人地址来决定是否下载或删除,从而造成系统资源的浪费。而IMAP协议不但可以克服POP3的缺陷,而且还提供了更强大的功能。

IMAP/IMAP4协议
协议简介

交互式邮件存取协议IMAP(Internet Mail Access Protocol)在RFC2060文档中定义,是为了弥补POP协议不足产生的新协议,目前使用的是第4个版本,所以也称为IMAP4,默认端口号143。支持在线、离线访问模式。它与POP3协议的主要区别在于用户可以不用把所有的邮件全部下载,支持通过客户端直接对服务器上的邮件进行操作。具体功能如下:

  1. 支持多个客户同时连接到一个邮箱。POP3协议假定邮箱当前的连接是唯一的连接。IMAP4协议允许多个用户同时访问邮箱同时提供一种机制让客户能够感知其他当前连接到这个邮箱的用户所做的操作。
  2. 支持访问消息中的MIME部分和部分获取。几乎所有的Internet 邮件都是以MIME格式传输的。MIME允许消息包含一个树型结构,这个树型结构的叶子节点都是单一内容类型而非叶子节点都是多块类型的组合。IMAP4协议允许客户端获取任何独立的MIME部分和获取信息的一部分或者全部。用户无需下载附件就可以浏览消息内容或者在获取内容的同时浏览。
  3. 支持在服务器保留消息状态信息。通过使用在IMAP4协议中定义的标志客户端可以跟踪消息状态,例如邮件是否被读取,回复,或者删除。这些标识存储在服务器,所以多个客户在不同时间访问一个邮箱可以感知其他用户所做的操作。
  4. 支持在服务器上访问多个邮箱。IMAP4客户端可以在服务器上创建,重命名,或删除邮箱(通常以文件夹形式显现给用户)。支持多个邮箱还允许服务器提供对于共享和公共文件夹的访问。支持服务器端搜索。IMAP4提供了一种机制给客户使客户可以要求服务器搜索符合多个标准的信息。在这种机制下客户端就无需下载邮箱中所有信息来完成这些搜索。
  5. 支持一个定义良好的扩展机制。吸取早期Internet协议的经验,IMAP的扩展定义了一个明确的机制。很多对于原始协议的扩展已被提议并广泛使用。

代码实现

使用SMTP发送邮件

# -*- coding:utf-8 -*-
import smtplib
# from email.mime.image import MIMEImage
# from email.mime.base import MIMEBase
# from email.mime.application import MIMEApplication
from email.header import Header
# import json
# import base64
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import formataddr

import email

username = '[email protected]'
password = '*****'
# 回信地址
replyto = '[email protected]'
# 显示的To收信地址
rcptto = ['[email protected]', '[email protected]']
# 显示的Cc收信地址
rcptcc = []
# Bcc收信地址,密送人不会显示在邮件上,但可以收到邮件
rcptbcc = []
# 全部收信地址,包含抄送地址,单次发送不能超过60人
receivers = rcptto + rcptcc + rcptbcc

# 构建alternative结构
msg = MIMEMultipart('alternative')
msg['Subject'] = Header('自定义信件主题')
msg['From'] = formataddr(["自定义发信昵称", username])  # 昵称+发信地址(或代发)
# list转为字符串
msg['To'] = ",".join(rcptto)
msg['Cc'] = ",".join(rcptcc)
msg['Reply-to'] = replyto  # 用于接收回复邮件,需要收信方支持标准协议
msg['Return-Path'] = '[email protected]'  # 用于接收退信邮件,需要收信方支持标准协议
msg['Message-id'] = email.utils.make_msgid()
msg['Date'] = email.utils.formatdate()


# 构建alternative的text/html部分
texthtml = MIMEText('自定义HTML超文本部分', _subtype='html', _charset='UTF-8')
msg.attach(texthtml)

if __name__ == '__main__':
    try:
        # client = smtplib.SMTP_SSL('smtpdm.aliyun.com', 465)
        # SMTP普通端口为25或80
        client = smtplib.SMTP('smtpdm.aliyun.com', 80)
        # client = smtplib.SMTP_SSL('smtpdm.aliyun.com', 465)
        # 开启DEBUG模式
        client.set_debuglevel(0)
        # 发件人和认证地址必须一致
        client.login(username, password)
        # 备注:若想取到DATA命令返回值,可参考smtplib的sendmail封装方法:
        client.sendmail(username, receivers, msg.as_string())  # 支持多个收件人,最多60个
        client.quit()
        print('邮件发送成功!')
    except smtplib.SMTPConnectError as e:
        print('邮件发送失败,连接失败:', e.smtp_code, e.smtp_error)
    except smtplib.SMTPAuthenticationError as e:
        print('邮件发送失败,认证错误:', e.smtp_code, e.smtp_error)
    except smtplib.SMTPSenderRefused as e:
        print('邮件发送失败,发件人被拒绝:', e.smtp_code, e.smtp_error)
    except smtplib.SMTPRecipientsRefused as e:
        print('邮件发送失败,收件人被拒绝:', e.smtp_code, e.smtp_error)
    except smtplib.SMTPDataError as e:
        print('邮件发送失败,数据接收拒绝:', e.smtp_code, e.smtp_error)
    except smtplib.SMTPException as e:
        print('邮件发送失败, ', str(e))
    except Exception as e:
        print('邮件发送异常, ', str(e))

使用IAMP接收邮件

from imapclient import IMAPClient
from email.header import decode_header
from pathlib import Path
from base64_util import safe_b64decode

class Imapmail(object):

    def __init__(self):  # 初始化数据
        self.serveraddress = None
        self.user = None
        self.passwd = None
        self.prot = None
        self.ssl = None
        self.timeout = None
        self.savepath = None
        self.server = None

    def client(self):  # 链接
        try:
            self.server = IMAPClient(self.serveraddress, self.prot, self.ssl, timeout=self.timeout)
            return self.server
        except BaseException as e:
            return "ERROR: >>> " + str(e)

    def login(self):  # 认证
        try:
            self.server.login(self.user, self.passwd)
        except BaseException as e:
            return "ERROR: >>> " + str(e)

    def getmaildir(self):  # 获取目录列表 [((), b'/', 'INBOX'), ((b'\\Drafts',), b'/', '草稿箱'),]
        dirlist = self.server.list_folders()
        return dirlist

    def getallmail(self):  # 收取所有邮件
        print(self.server)
        self.server.select_folder('INBOX', readonly=True)  # 选择目录 readonly=True 只读,不修改,这里只选择了 收件箱
        result = self.server.search()  # 获取所有邮件总数目 [1,2,3,....]
        print("邮件列表:", result[-10:])
        for _sm in result[-10:]:
            msgdict = self.server.fetch(_sm, ['BODY[]'])  # 获取邮件内容
            mailbody = msgdict[_sm][b'BODY[]']  # 获取邮件内容
            path_save = Path(self.savepath, "email")
            path_save.mkdir(parents=True, exist_ok=True)
            file_save = Path(path_save, str(_sm))
            file_save.open("wb").write(mailbody)

    def close(self):
        self.server.close()


if __name__ == "__main__":
    imap = Imapmail()
    imap.serveraddress = "imaphm.***.com"  # 邮箱地址
    imap.user = "[email protected]"
    imap.passwd = "*****"
    imap.savepath = "*****/***"  # 邮件存放路径
    imap.client()
    imap.login()
    imap.getallmail()
    imap.close()

总结

在邮件开发过程中,全局的认知应该是,STMP协议用来发送邮件,POP/POP3是早期版本邮件接收协议,IAMP/IAMP4是功能更强大的邮件协议。在开发过程中更值得关注的是MIME协议,因为其规定了富文本的邮件格式。

参考资料

  • https://blog.csdn.net/bripengandre/article/details/2192982

  • https://www.cnblogs.com/wfwenchao/p/5208926.html

  • https://blog.csdn.net/kerry0071/article/details/28604267

  • https://www.163.com/dy/article/ECQKEMF7053179V7.html

你可能感兴趣的:(服务器,网络,java)