POP邮件协议的优点在于它是一个开放的标准,有着完善的文档,这就使得编写POP邮件客户程序不那么困难,只要掌握了POP、SMTP的基础知识,就可以写出代理程序来执行各种任务,例如过滤广告和垃圾邮件,或提供e-mail自动应答服务。

  Hotmail是世界上影响最广的Web邮件系统,遗憾的是,当我们要为Hotmail编写独立的客户程序(不通过浏览器访问的客户程序)时,马上就会遇到Hotmail不提供POP网关这一障碍。

  虽然Hotmail不提供POP支持,但浏览器并非访问Hotmail的唯一途径。例如,利用Outlook Express可以直接连接到标准的Hotmail或MSN信箱,提取、删除、移动或发送邮件。利用HTTP包监视器,我们可以监视到Outlook Express和Hotmail的通信过程,分析出客户程序如何连接到Hotmail信箱。

  Outlook Express利用了一种通常称为HTTPMail的未公开的协议,借助一组HTTP/1.1扩展访问Hotmail。本文将介绍HTTPMail的一些特点以及利用C#客户程序访问Hotmail的过程。本文的示例程序利用COM互操作将XMLHTTP用作一种传输服务。XMLHTTP组件提供了一个完善的HTTP实现,除了包括认证功能,还能够在发送HTTP请求给服务器之前设置定制的HTTP头。

  一、连接HTTPMail网关

  Hotmail信箱默认的HTTPMail网关在http://services.msn.com/svcs/hotmail/httpmail.asp。HTTPMail协议实际上是一个标准的WebDAV服务,只不过尚未公开而已。在编写C#程序时,我们可以方便地调用.NET框架在System.Net名称空间中提供的各个TCP和HTTP类。另外,由于我们要操作WebDAV,在C#环境下利用XMLHTTP连接Hotmail最为简便,只需引用一下MSXML2组件就可以直接访问。注意在本文的代码片断中,带有下滑线后缀的变量是示例代码中声明的成员域:

// 获得名称空间using MSXML2;...// 创建对象xmlHttp_ = new XMLHTTP();

  为了连接到安全服务器,WebDAV协议要求执行HTTP/1.1验证。HTTPMail客户程序发出的第一个请求利用WebDAV PROPFIND方法查找一组属性,其中包括Hotmail广告条的URL以及信箱文件夹的位置:

<?xml version="1.0"?><D:propfind xmlns:D="DAV:" xmlns:h="http://schemas.microsoft.com/hotmail/"
xmlns:hm="urn:schemas:httpmail:"><D:prop><h:adbar/><hm:contacts/><hm:inbox/><hm:outbox/><hm:sendmsg/><hm:sentitems/><hm:deleteditems/><hm:drafts/><hm:msgfolderroot/><h:maxpoll/><h:sig/></D:prop></D:propfind>

  通过XMLHTTP发送第一个请求时,首先指定WebDAV服务器URL,然后生成XML请求的内容:

// 指定服务器的URLstring serverUrl = "http://services.msn.com/svcs/hotmail/httpmail.asp";// 构造查询string folderQuery = null;folderQuery += "<?xml version='1.0'?><D:propfind xmlns:D='DAV:' ";folderQuery += "xmlns:h='http://schemas.microsoft.com/hotmail/' ";folderQuery += "xmlns:hm='urn:schemas:httpmail:'><D:prop><h:adbar/>";folderQuery += "<hm:contacts/><hm:inbox/><hm:outbox/><hm:sendmsg/>";folderQuery += "<hm:sentitems/><hm:deleteditems/><hm:drafts/>";folderQuery += "<hm:msgfolderroot/><h:maxpoll/><h:sig/></D:prop></D:propfind>";

  XMLHTTP组件提供了一个open()方法来建立与HTTP服务器的连接:

void open(string method, string url, bool async, string user, string password);

  open()方法的第一个参数指定了用来打开连接的HTTP方法,例如GET、POST、PUT或PROPFIND,通过这些HTTP方法我们可以提取文件夹信息、收集邮件或发送新邮件。为连接到Hotmail网关,我们指定用PROPFIND方法来查询信箱。注意open()方法允许执行异步调用(默认启用),对于带图形用户界面的邮件客户程序来说,异步调用是最理想的调用方式。由于本文的示例程序是一个控制台应用,我们把这个参数设置成false。

  为了执行身份验证,我们在open()方法中指定了用户名字和密码。在使用XMLHTTP组件时,如果open()方法没有提供用户名字和密码参数,但网站要求执行身份验证,XMLHTTP将显示出一个登录窗口。为了打开通向Hotmail网关的连接,我们把PROPFIND请求的头设置成XML查询的内容,消息的正文保持空白,然后发送消息:

// 打开一个通向Hotmail服务器的连接xmlHttp_.open("PROPFIND", serverUrl, false, username, password);// 发送请求xmlHttp_.setRequestHeader("PROPFIND", folderQuery);xmlHttp_.send(null);

  二、分析信箱的文件夹列表

  发送给services.msn.com的请求通常要经历几次重定向,经过服务器端的负载平衡处理,最后请求会被传递到一个空闲的Hotmail服务器,并执行身份验证。在客户端,这个重定向、执行身份验证的交互过程由XMLHTTP组件负责处理。成功建立连接后,服务器还会要求设置一些Cookie、验证当前会话的合法性,这部分工作同样也由XMLHTTP组件自动处理。初始的连接请求发出之后,服务器将返回一个XML格式的应答:

// 获得应答的内容string folderList = xmlHttp_.responseText;

  服务器返回的应答包含许多有用的信息,其中包括信箱中文件夹的URL位置,下面是一个例子:

<?xml version="1.0" encoding="Windows-1252"?><D:response>...<D:propstat><D:prop><h:adbar>AdPane=Off*...</h:adbar><hm:contacts>http://law15.oe.hotmail.com/...</hm:contacts><hm:inbox>http://law15.oe.hotmail.com/...</hm:inbox><hm:sendmsg>http://law15.oe.hotmail.com/...</hm:sendmsg><hm:sentitems>http://law15.oe.hotmail.com/...</hm:sentitems><hm:deleteditems>http://law15.oe.hotmail.com/...</hm:deleteditems><hm:msgfolderroot>http://law15.oe.hotmail.com/...</hm:msgfolderroot>...</D:prop></D:response></D:multistatus>

  在本文的控制台示例程序中,我们感兴趣的两个文件夹是收件箱和发件箱的文件夹,它们分别用于接收和发送邮件。

  在C#环境中解析XML的方法很多,由于我们肯定代码涉及的所有XML文档总是合法的,所以可以利用System.XML.XmlTextReader速度快的优势。XmlTextReader是一个"只向前"的读取器,下面把XML字符数据转换成字符流,初始化XML读取器:

// 初始化inboxUrl_ = null;sendUrl_ = null;// 装入XMLStringReader reader = new StringReader(folderList);XmlTextReader xml = new XmlTextReader(reader);

  遍历各个节点,选取出hm:inbox和hm:sendmsg节点,这两个节点分别代表收件箱和发件箱:

// 读取XML数据while(xml.Read()){// 是一个XML元素?if(xml.NodeType == XmlNodeType.Element){// 获取该节点string name = xml.Name;// 该节点代表收件箱?if(name == "hm:inbox"){// 保存收件箱URLxml.Read();inboxUrl_ = xml.Value;}// 该节点代表发件箱?if(name == "hm:sendmsg"){// 保存发件箱URLxml.Read();sendUrl_ = xml.Value;}}}

  只有先获取当前这次会话的合法的收件箱和发件箱URL,才可以发送和接收邮件。

  三、列举文件夹内容

  得到了信箱文件夹(如收件箱)的URL之后,就可以向该文件夹的URL发送WebDAV请求列举其内容。示例程序定义了一个托管类型MailItem,用来保存文件夹里一项内容(即一个邮件)的信息。文件夹内容列举从初始化一个MailItems数组开始:

// 初始化ArrayList mailItems = new ArrayList();

  为获得邮件主题、收件人地址、发件人地址之类的邮件基本信息,我们要用到下面XML格式的WebDAV查询:

<?xml version="1.0"?><D:propfind xmlns:D="DAV:" xmlns:hm="urn:schemas:httpmail:" xmlns:m="urn:schemas:mailheader:"><D:prop><D:isfolder/><hm:read/><m:hasattachment/><m:to/><m:from/><m:subject/><m:date/><D:getcontentlength/></D:prop></D:propfind>

  生成上述XML查询字符串的C#代码:

// 构造查询string getMailQuery = null;getMailQuery += "<?xml version='1.0'?><D:propfind xmlns:D='DAV:' ";getMailQuery += "xmlns:hm='urn:schemas:httpmail:' ";getMailQuery += "xmlns:m='urn:schemas:mailheader:'><D:prop><D:isfolder/>";getMailQuery += "<hm:read/><m:hasattachment/><m:to/><m:from/><m:subject/>";getMailQuery += "<m:date/><D:getcontentlength/></D:prop></D:propfind>";

  就象前面获取信箱文件夹清单的方式一样,上述请求也通过XMLHTTP用PROPFIND方法发送,这次我们把请求的正文设置成查询字符串。由于当前会话的用户身份已经通过验证,所以XMLHTTP open()调用中不必再提供用户名字和密码:

// 获取邮件信息xmlHttp_.open("PROPFIND", folderUrl, false, null, null);xmlHttp_.send(getMailQuery);string folderInfo = xmlHttp_.responseText;

  如果请求成功,服务器返回的应答XML流包含了该文件夹中各个邮件的信息:

<D:multistatus><D:response><D:href>http://sea1.oe.hotmail.com/cgi-bin/hmdata/...</D:href> <D:propstat><D:prop><hm:read>1</hm:read> <m:to/> <m:from>Mark Anderson</m:from> <m:subject>RE: New Information</m:subject> <m:date>2002-08-06T16:38:39</m:date> <D:getcontentlength>1238</D:getcontentlength> </D:prop><D:status>HTTP/1.1 200 OK</D:status> </D:propstat></D:response>...

  观察服务器返回的应答,我们发现每一个节点包含一组标识邮件的域,例如通过标记可提取出邮件。下面我们再次使用System.XML.XmlTextReader解析这个XML数据流,首先初始化流读取器:

MailItem mailItem = null;// 装入XMLStringReader reader = new StringReader(folderInfo);XmlTextReader xml = new XmlTextReader(reader);

  四、分析邮件基本信息

  为了遍历一次就解析好整个XML文档,我们在每次打开元素时就创建一个新的MailItem实例,一遇到标记的末尾就保存该实例,在此期间,我们提取并设置MailItem的域:

// 读取XML数据while(xml.Read()){string name = xml.Name;XmlNodeType nodeType = xml.NodeType;// 是一个email?if(name == "D:response"){// 开始?if(nodeType == XmlNodeType.Element){// 创建一个新的MailItemmailItem = new MailItem();}// 结束?if(nodeType == XmlNodeType.EndElement){// 保存emailmailItems.Add(mailItem);// 清除变量mailItem = null;}}// 是一个元素?if(nodeType == XmlNodeType.Element){// 邮件的URL属性if(name == "D:href"){// 继续读取xml.Read();mailItem.Url = xml.Value;}// 邮件的"已阅读"属性if(name == "hm:read"){// 继续读取xml.Read(); mailItem.IsRead = (xml.Value == "1");}// 其他MailItem的属性...}}

  上面的代码枚举指定文件夹内的每一个MailItem,分别提取各个MailItem的下列属性:

XML节点  说明
D:href  用来提取邮件的URL
hm:read  如果邮件已阅读,则该标记被设置
m:to  收件人
m:from  发件人
m:subject  邮件主题
m:date  时间标记
D:getcontentlength  邮件的大小(字节数)

  五、接收邮件

  枚举出文件夹里面的MailItem之后,我们就可以利用MailItem的URL获得邮件本身,只需要向Hotmail服务器发送一个HTTP/1.1 GET请求就可以了。示例代码中的LoadMail()函数输入一个MailItem实例作为参数,返回邮件的内容:

/// <summary>/// 下载MailItem指定的邮件/// </summary>public string LoadMail(MailItem mailItem){// 邮件的URLstring mailUrl = mailItem.Url;// 打开Hotmail服务器连接xmlHttp_.open("GET", mailUrl, false, null, null);// 发送请求xmlHttp_.send(null);// 获取应答string mailData = xmlHttp_.responseText;// 返回邮件数据return mailData;}

  六、发送邮件

  LoadMail()方法通过发送HTTP/1.1 GET请求获取邮件,类似地,用Hotmail发件箱发送邮件时我们提交一个POST请求,如下面的SendMail()方法所示。

/// <summary>/// 发送一个邮件/// </summary>public void SendMail(string from, string fromName,string to, string subject, string body){...}

  首先准备好后面要用到的引号字符以及邮件的时间标记:

// 引号字符string quote = "\u0022";// 时间标记DateTime now = DateTime.Now;string timeStamp = now.ToString("ddd, dd MMM yyyy hh:mm:ss");

  HTTPMail协议采用与SMTP相似的通信模式。Outlook Express用MIME格式发送邮件,但为简单计,本例我们只发送纯文本的邮件:

// 构造POST请求的内容string postBody = null;// 邮件头.postBody += "MAIL FROM:<" + from + ">\r\n";postBody += "RCPT TO:<" + to + ">\r\n";postBody += "\r\n";postBody += "From: " + quote + fromName + quote + " <" + from + ">\r\n";postBody += "To: <" + to + ">\r\n";postBody += "Subject: " + subject +"\r\n";postBody += "Date: " + timeStamp + " -0000\n";postBody += "\r\n";// 邮件正文postBody += body;

  发送邮件时,我们要把Content-Type请求头设置成message/rfc821,表示这个请求包含一个遵从RFC821的消息。最后要做的就是把邮件发送到服务器:

// 打开连接xmlHttp_.open("POST", sendUrl_, false, null, null);// 发送请求xmlHttp_.setRequestHeader("Content-Type", "message/rfc821");xmlHttp_.send(postBody);

  只要目标地址正确无误,Hotmail就会把邮件发送到目的地。

  结束语:

  Hotmail是世界上最大的免费Web邮件提供商。但是,Hotmail使用的HTTPMail协议是非公开的,从而为编写直接访问Hotmail的客户程序带来了困难。本文示范了如何在C#环境中利用XMLHTTP组件直接连接到Hotmail,以及如何发送和接收邮件,证明了通过HTTPMail连接Hotmail可以做到象使用POP3、IMAP4、SMTP等协议一样简单。

转载于:https://www.cnblogs.com/xioxu/archive/2008/05/06/1185526.html

用C#访问Hotmail -转相关推荐

  1. 通过JavaMail访问Hotmail邮箱

    [size=large]http://blog.csdn.net/changlich/archive/2007/01/01/1472089.aspx 相信许多人都有msn聊天工具的帐号,例如abc@h ...

  2. 通过JavaMail访问Hotmail邮箱 .

    相信许多人都有msn聊天工具的帐号,例如abc@hotmail.com ,这个账号其实也是一个邮件地址,可以进行正常的邮件收发功能,你可以通过网址 http://www.hotmail.com 来访问 ...

  3. JavaMail 访问Hotmail

    Hotmail使用的是WebDAV来提供基于HTTP协议的应用编程接口,WebDAV(Web 分布式创作和版本控制)已经成为重要的 Web 通讯协议.WebDAV所要解决的问题是: 1.改写保护:HT ...

  4. 使用微软DNS服务器解决Hotmail、微软账户登录页面无法访问的问题

    近日,在重装Windows 10系统之后,发现Edge浏览器无法登录微软账号做数据同步,具体表现为登录页面无法打开.此前,通过浏览器访问hotmail等页面的时候,也经常发生页面无法打开或者打开及其缓 ...

  5. Powershell RESTAPI 访问Office365

    豆子之前对Office365的用户操作一般是通过远程的加载模块,然后创建一个session连接. 比如 $cred = Get-Credential Import-Module MSOnline Se ...

  6. hotmail_在新的Hotmail Wave 4中禁用Messenger

    hotmail Are you annoyed by having Messenger automatically sign in when you're reading your emails in ...

  7. 如何使用SkyDrive的25 GB作为映射驱动器以方便访问

    SkyDrive is an online storage system included in Windows Live, which gives you 25 GB of space that y ...

  8. Java使用hotmail的SMTP服务器转发邮件出错

    最近使用hotmail的SMTP在服务器上转发邮件,后台获得如下错误: javax.mail.AuthenticationFailedException: 535 5.0.0 Authenticati ...

  9. hotmail邮箱不能打开解决方法

    hotmail在升级,也有说是由于微软正在宣传新产品WindowsLive系统,所以将所有登陆程序从Passport网站转移到Live网站.由于国内运营商还没有将Live网站加入可访问区域,造成登陆系 ...

最新文章

  1. 实现php实现价格的排序,php 数组动态添加实现代码(最土团购系统的价格排序)_PHP教程...
  2. 【数字信号处理】相关系数 ( 相关系数概念解析 | 信号能量常数 | 共轭序列 | 序列在相同时刻的相关性 )
  3. expdp / impdp 用法详解
  4. Docker 使用Dockerfile构建Docker(三)
  5. 韦老师的开发板和嵌入式书籍赠送
  6. VS2010开发应用程序读写注册表
  7. Mybatis中#{}与${}的使用
  8. 很多大企业都在用的Pentaho 商业分析软件中存在多个严重缺陷
  9. SqlServer-RBAC五表权限
  10. 嵌入式Ubuntu 搭建caffee环境
  11. 网页版pdf转换方法
  12. 愚人节,你的微信公众号图文排版够愚吗?
  13. VMware Funsion 8.5.1破解版
  14. java.lang.Byte常用方法
  15. Python学习:垃圾回收机制
  16. 国内产线 OLED 良率低,产能释放缓慢
  17. VisualAssistX无法加载,加载问题异常解决
  18. 第7章第20节:单图排版:使用巨型字母分隔整张图片 [PowerPoint精美幻灯片实战教程]
  19. 全球与中国湿式剃须刀市场深度研究分析报告
  20. c语言解引用运算符,C++ 解引用(*)和箭头(-)运算符的重载

热门文章

  1. mysql 返回mysql,mysql函数返回表
  2. Java高并发编程:总线锁定和缓存一致性的问题
  3. 计算机二级7月考试,2020年计算机二级MS Office考试每日一练(7月27日)
  4. 如何在柱状图中点连线_练瑜伽,如何放松僵硬紧张的髂腰肌?
  5. w3wp进程发生死锁ISAPI aspnet
  6. javascript Navigator对象属性和方法
  7. openssl生成rsa密钥对和密钥格式转换
  8. AIX下RAC搭建 Oracle10G(一)检測系统环境
  9. GO 输出字符数同时输出这个字符串的字节数
  10. CPython对象模型:string(留坑待填)