C#利用Lumisoft组件实现收邮解析邮件功能

一、 POP3协议简介

1、什么是POP3协议:
邮局协议(Post Office Protocol,简称POP)是TCP/IP协议族中的一员,由RFC 1939 定义。本协议主要用于支持使用客户端远程管理在服务器上的电子邮件。最新版本为POP3,全名“Post Office Protocol - Version 3”,而提供了SSL加密的POP3协议被称为POP3S。
2、POP3协议具体过程:
POP 协议支持“离线”邮件处理。其具体过程是:邮件发送到服务器上,电子邮件客户端调用邮件客户机程序以连接服务器,并下载所有未阅读的电子邮件。这种离线访问模式是一种存储转发服务,将邮件从邮件服务器端送到个人终端机器上,一般是PC机或 MAC。一旦邮件发送到 PC 机或MAC上,邮件服务器上的邮件将会被删除。但目前的POP3邮件服务器大都可以“只下载邮件,服务器端并不删除”,也就是改进的POP3协议。
3、电子邮件系统的基本网络结构
C#利用Lumisoft组件实现收邮解析邮件功能

二、 运用C#实现POP3接收邮件的三种方法

1、 利用Socket组件
socket本质是编程接口,是对TCP/IP的封装。TCP/IP是传输层的协议。POP3是应用层的协议,是基于TCP/IP协议的。所以,使用socket实现上述几种协议的客户端,其实是对借助socket对TCP/IP数据传输的封装基础,再往上封装一层的。
POP3只需要建立一个连接,客户端向服务器发命令,服务器向客户端返回数据就行了。具体就是通过socket发命令,再通过socket接数据 。
具体实现如下图(实现代码不再详细阐述):
C#利用Lumisoft组件实现收邮解析邮件功能
该POP3客户端主要实现的就是接收邮件功能了,然而并没有实现邮件内容解析功能。
并且在连接过程中,163、126、sina等邮箱均可以登录至相应的pop服务器,唯独QQ邮箱受阻,提示信息为:-err login fail a secure connection is required(such as ssl),所以需要建立ssl的相关安全连接才可以登录至pop服务器,因此放弃该方法,所以选择方法三
2、 利用OpenPop组件
这是一款可以支持 SSL 收发邮件的控件,和LumiSoft组件类似,自己在使用LumiSoft之前对OpenPop组件进行了测试,其中邮件解析的各项数据较全。但是OpenSmtp组件连接不了ssl,这个只对pop协议适用,而LumiSoft对所有的邮件传输协议均适用,所以选择了方法三。
运行效果如下图:
C#利用Lumisoft组件实现收邮解析邮件功能
3、 利用LumiSoft组件
LumiSoft.Net是非常强大的开源组件,不仅仅发送邮件,同样也可用于接收邮件,在这周的学习时间里是个人找到的最好的开源组件了。阅读LumiSoft.Net的源代码,可以看到LumiSoft.Net编程严格遵循了RFC(Request For Comments)定义的协议规范。通过阅读这些源码对于了解RFC和其中关于邮件网络协议规范也是非常有帮助的。
如果要查阅RFC文档可以通过LumiSoft组件链接网站查询。
运行效果如下图:
主界面:
C#利用Lumisoft组件实现收邮解析邮件功能
解析:
C#利用Lumisoft组件实现收邮解析邮件功能

三、 运用LumiSoft组件实现接收邮件

1、 运用的命名空间

using System
using System.Windows.Forms;
using System.IO;
using LumiSoft.Net;
using LumiSoft.Net.Mail;
using LumiSoft.Net.POP3.Client;
using LumiSoft.Net.MIME;

2、 连接并获取信息部分
(1)、连接到POP3服务器
POP3_Client.Connect函数主要是连接pop服务器,端口,是否用ssl
POP3_Client.Authenticate函数主要验证邮箱和邮箱授权码(通常为16位)

private void btn_connect_Click(object sender, EventArgs e)
        {
            if (txt_username.Text == "")
            {
                MessageBox.Show(this, "Please fill user name !", "Error:", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            POP3_Client pop3 = new POP3_Client();
            try
            {
                pop3.Connect(txt_server.Text, (int)number_port.Value, cb_isSSL.Checked);
                
                pop3.Authenticate(txt_username.Text, txt_password.Text, true);

                m_pPop3 = pop3;
                this.DialogResult = DialogResult.OK;

                FillMessagesList();
            }
            catch (Exception x)
            {
                MessageBox.Show(this, "POP3 server returned: " + x.Message + " !", "Error:", MessageBoxButtons.OK, MessageBoxIcon.Error);
                pop3.Dispose();
            }
        }

(2)、获取邮件头信息函数FillMessagesList()
通过POP3_ClientMessage、Mail_Message获取信息,并写进ListViewItem的项内,然后传递给lst_message的Item,以达到在窗体中显示的目的。

private void FillMessagesList()
        {
            this.Cursor = Cursors.WaitCursor;
            try
            {
                foreach (POP3_ClientMessage message in m_pPop3.Messages)
                {
                    Mail_Message mime = Mail_Message.ParseFromByte(message.HeaderToByte());
                    Mail_Message mime2 = Mail_Message.ParseFromByte(message.MessageToByte());
                    ListViewItem item = new ListViewItem();
                    if (mime.From != null)
                    {
                        item.Text = mime.From.ToString();
                       
                    }
                    else
                    {
                        item.Text = "<none>";
                    }
                    
                    if (string.IsNullOrEmpty(mime.Subject))
                    {
                        item.SubItems.Add("<none>");
                    }
                    else
                    {
                        item.SubItems.Add(mime.Subject);
                    }
                    item.SubItems.Add(mime.Date.ToString());
                    item.SubItems.Add(((decimal)(message.Size / (decimal)1000)).ToString("f2") + " kb");
                    
                    if (mime.Cc != null)
                    {
                        item.SubItems.Add(Convert.ToString(mime.Cc));
                    }
                    else
                    {
                        item.SubItems.Add("<none>");
                    }
                    if (mime.Bcc != null)
                    {
                        item.SubItems.Add(Convert.ToString(mime.Bcc));
                    }
                    else
                    {
                        item.SubItems.Add("<none>");
                    }
                    item.SubItems.Add(mime.ContentTransferEncoding);
                    item.SubItems.Add(mime.MessageID);
                    item.SubItems.Add(mime.MimeVersion);
                    item.SubItems.Add(Convert.ToString(mime.Received));
                    item.SubItems.Add(mime2.BodyHtmlText);
                    item.SubItems.Add(Convert.ToString(mime.ContentType));
                   
                    item.SubItems.Add(Convert.ToString(mime.IsDisposed));
                    item.SubItems.Add(Convert.ToString(mime.IsModified));
                    

                    item.Tag = message;
                    lst_messages.Items.Add(item);
                }
            }
            catch (Exception x)
            {
                MessageBox.Show(this, "Error: " + x.Message, "Error:", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            this.Cursor = Cursors.Default;
        }

(3)、SSL判断函数
通过WellKnownPorts方法自动获取各个邮件服务器所在ssl或者非ssl状态下对应的端口。

private void cb_isSSL_CheckedChanged_1(object sender, EventArgs e)
        {
            if (cb_isSSL.Checked)
            {
                number_port.Value = WellKnownPorts.POP3_SSL;
            }
            else
            {
                number_port.Value = WellKnownPorts.POP3;
            }
        }

3、 通过窗体获取具体解析的信息部分
(1)、邮件列表内点击行为
通过点击邮件列表内的邮件项这一选择行为,会相应地在下面附件栏和正文栏显示出对应的信息,并把邮件头解析信息返回至txt_Header,其中用到了POP3_ClientMessage、Mail_Message、MIME_Entity方法。

private void lst_messages_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.Cursor = Cursors.WaitCursor;
            try
            {
                toolStrip1.Items["save"].Enabled = false;
                toolStrip1.Items["delete"].Enabled = false;
                if (lst_messages.SelectedItems.Count > 0)
                {
                    lst_attachment.Items.Clear();
                    txt_Body.Text = "";

                    POP3_ClientMessage message = (POP3_ClientMessage)lst_messages.SelectedItems[0].Tag;
                    Mail_Message mime = Mail_Message.ParseFromByte(message.MessageToByte());

                    string Sender = mime.From == null ? "Sender is null" : mime.From[0].DisplayName;
                    string senderAddress = mime.From == null ? "senderAddress is null" : mime.From[0].Address;
                    string subject = mime.Subject ?? "subject is null";
                    string recDate = mime.Date == DateTime.MinValue ? "date not specified" : mime.Date.ToString();
                    string content = mime.BodyText ?? "content is null";

                    

                    foreach (MIME_Entity entity in mime.Attachments)
                    {
                        ListViewItem item = new ListViewItem();
                        if (entity.ContentDisposition != null && entity.ContentDisposition.Param_FileName != null)
                        {
                            item.Text = entity.ContentDisposition.Param_FileName;
                        }
                        else
                        {
                            item.Text = "untitled";
                        }
                        item.Tag = entity;
                        lst_attachment.Items.Add(item);
                    }

                    if (mime.BodyText != null)
                    {
                        txt_Body.Text = mime.BodyText;
                    }
                    if (mime.Header != null)
                    {
                    txt_Header.Text = Convert.ToString(mime.Header);
                    }
                    toolStrip1.Items["save"].Enabled = true;
                    toolStrip1.Items["delete"].Enabled = true;
                }
            }
            catch (Exception x)
            {
                MessageBox.Show(this, "Error: " + x.Message, "Error:", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            this.Cursor = Cursors.Default;
        }

(2)、邮件列表内保存及删除行为
在toolStrip上生成两个button,分别对应保存及删除。

private void toolStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            if (string.Equals(e.ClickedItem.Name, "save"))
            {
                SaveFileDialog dlg = new SaveFileDialog();
                dlg.FileName = "message.eml";
                if (dlg.ShowDialog() == DialogResult.OK)
                {
                    this.Cursor = Cursors.WaitCursor;
                    POP3_ClientMessage message = (POP3_ClientMessage)lst_messages.SelectedItems[0].Tag;
                    File.WriteAllBytes(dlg.FileName, message.MessageToByte());
                    this.Cursor = Cursors.Default;
                }
            }
            else if (string.Equals(e.ClickedItem.Name, "delete"))
            {
                if (MessageBox.Show(this, "Do you want to delete selected message ?", "Confirm Delete:", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    POP3_ClientMessage message = (POP3_ClientMessage)lst_messages.SelectedItems[0].Tag;
                    message.MarkForDeletion();
                    lst_messages.SelectedItems[0].Remove();
                }
            }
        }

(3)、附件列表内点击行为
通过鼠标右键点击“Save”可以把附件下载至本地
1、右键点击生成菜单函数:

private void lst_attachment_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right && lst_attachment.SelectedItems.Count > 0)
            {
                ContextMenuStrip menu = new ContextMenuStrip();
                menu.Items.Add("Save");
                menu.ItemClicked += new ToolStripItemClickedEventHandler(menu_ItemClicked);
                menu.Show(Control.MousePosition);
            }
        }

2、点击“Save”保存函数:

private void menu_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            MIME_Entity entity = (MIME_Entity)lst_attachment.SelectedItems[0].Tag;
            SaveFileDialog dlg = new SaveFileDialog();
            dlg.FileName = lst_attachment.SelectedItems[0].Text;
            if (dlg.ShowDialog(this) == DialogResult.OK)
            {
                File.WriteAllBytes(dlg.FileName, ((MIME_b_SinglepartBase)entity.Body).Data);
            }
        }

Lumisoft下函数部分属性附表

AcceptLanguage  获取或设置邮件发送方请求用于响应的语言。值null表示未指定。

AllEntities	获取所有MIME实体作为列表。(继承自MIME_Message。)

Attachments	获取此邮件附件。

Bcc	        获取或设置密送收件人邮箱。值null表示未指定。

Body	        获取或设置MIME实体主体。(继承自MIME_Entity。)

BodyHtmlText	获取邮件体html文本。如果没有可用的正文html文本,则返回null。

BodyText	获取邮件正文文本。如果没有可用的正文,则返回null。

BodyTextEncoding	获取邮件正文文本编码。如果没有可用的正文,则返回null。

Cc	        获取或设置抄送收件人邮箱。值null表示未指定。

Comments	获取或设置有关邮件的其他注释。值null表示未指定。

ContentAlternative	获取或设置邮件替代内容。值null表示标题字段不存在。(继承自MIME_Entity。)

ContentBase	获取或设置用于解析此内容部分中的相对URI的基础。值null表示标题字段不存在。(继承自MIME_Entity。)

ContentDescription	获取或设置邮件正文部分的描述。值null表示标题字段不存在。在RFC 2045中定义8。(继承自MIME_Entity。)

ContentDisposition	获取或设置内容处置。值null表示标题字段不存在。(继承自MIME_Entity。)

ContentDuration	获取或设置内容的持续时间。值null表示标题字段不存在。(继承自MIME_Entity。)

Contentfeatures	获取或设置MIME正文部分的内容功能。值null表示标题字段不存在。(继承自MIME_Entity。)

ContentID	获取或设置内容正文部件ID。值null表示标题字段不存在。在RFC 2045 7中定义。(继承自MIME_Entity。)

ContentLanguage	获取或设置邮件内容的语言。值null表示标题字段不存在。(继承自MIME_Entity。)

ContentLocation	获取或设置用于检索正文部分的URI。值null表示标题字段不存在。(继承自MIME_Entity。)

ContentMD5	获取或设置内容MD5校验和。值null表示标题字段不存在。(继承自MIME_Entity。)

ContentTransferEncoding	获取或设置内容传输编码。值null表示标题字段不存在。RFC定义的值在MIME_TransferEncodings中。在RFC 2045中定义6。(继承自MIME_Entity。)

ContentType	获取或设置MIME内容类型。值null表示标题字段不存在。在RFC 2045中定义5。(继承自MIME_Entity。)

Date	        获取或设置邮件日期和时间。值DateTime.MinValue表示未指定。

DispositionNotificationOptions	获取或设置处置通知选项。值null表示未指定。

DispositionNotificationTo	获取或设置邮箱发送处置通知邮件的位置。值null表示未指定。

From	        获取或设置邮件作者。值null表示未指定。

Header	        获取MIME实体头字段集合。(继承自MIME_Entity。)

Importance	获取或设置邮件重要性。值null表示未指定。

InReplyTo	获取或设置标识已回复的邮件。值null表示未指定。

IsDisposed	获取是否释放此对象。(继承自MIME_Entity。)

IsModified	获取此实体是否已加载后进行修改。(继承自MIME_Entity。)

IsSigned	获取邮件是否包含签名数据。(继承自MIME_Message。)

Keywords	获取或设置邮件关键字和/或短语。值null表示未指定。

ListArchive	获取或设置邮件列表归档的URL。值null表示未指定。

ListHelp	获取或设置邮件列表信息的URL。值null表示未指定。

ListID	        获取或设置邮件列表标识符。值null表示未指定。

ListOwner	获取或设置邮件列表所有者邮箱的URL。值null表示未指定。

ListPost	获取或设置邮件列表发布的URL。值null表示未指定。

ListSubscribe	获取或设置邮件列表订阅的URL。值null表示未指定。

ListUnsubscribe	获取或设置邮件列表unsubscription的URL。值null表示未指定。

MessageContext	获取或设置邮件的类型或上下文。值null表示未指定。

MessageID       获取或设置邮件标识符。值null表示未指定。

MimeVersion	获取或设置MIME版本号。值null表示标题字段不存在。通常这个值是1.0。在RFC 2045第4节中定义。(继承自MIME_Entity。)

OriginalMessageID	获取或设置原始邮件标识符。值null表示未指定。

Parent	        获取此实体的父实体,如果这是根实体,则返回null。(继承自MIME_Entity。)

PICSLabel	获取或设置PICS评级标签。值null表示未指定。

Priority	获取或设置邮件优先级。值null表示未指定。

Received	获取邮件传输跟踪信息。值null表示未指定。

References	获取或设置相关的邮件标识符。值null表示未指定。

ReplyTo	获取或设置邮箱回复的邮箱。值null表示未指定。

ResentBcc	获取或设置重发时邮件被bcc的邮箱。值null表示未指定。

ResentCc	获取或设置重新发送邮件的邮箱。值null表示未指定。

ResentDate	获取或设置重新发送的日期和时间邮件。值DateTime.MinValue表示未指定。

ResentFrom	获取或设置重新发送邮件的人的邮箱。值null表示未指定。

ResentMessageID	获取或设置重新发送邮件的邮件标识符。值null表示未指定。

ResentReplyTo	获取或设置重新发送的回复。值null表示未指定。

ResentSender	获取或设置实际重新发送邮件的人的邮箱。值null表示未指定。

ResentTo	获取或设置重新发送邮件的邮箱。值null表示未指定。

ReturnPath这样	获取或设置邮件返回路径。值null表示未指定。

Sender	        获取或设置邮件发件人。值null表示未指定。

Subject	        获取或设置邮件主题。值null表示未指定。

To	        获取或设置邮件主要收件人。值null表示未指定。

总结

1、QQ、163、sina等主流邮箱均可以登录。
2、通过解析可以获取邮件的基本信息,比如发件人、邮件正文编码方式、抄送人、主题、大小、时间、ip地址等等。
3、可以执行邮件的删除和下载行为,并可以下载附件,附件的格式没有影响,exe,jpg,pdf均可下载。