首页 > 编程语言 >用C#访问Hotmail

用C#访问Hotmail

时间:2023-07-02 11:06:14浏览次数:69  
标签:XML xml Hotmail string C# 访问 null 邮件

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以及信箱文件夹的位置: 

  
  
  
xmlns:hm="urn:schemas:httpmail:">


     
     
     
     
     
     
     
     
     
     
     
   
 

通过XMLHTTP发送第一个请求时,首先指定WebDAV服务器URL,然后生成XML请求的内容: 
// 指定服务器的URL 
  string serverUrl = "http://services.msn.com/svcs/hotmail/httpmail.asp"; 
  // 构造查询 
  string folderQuery = null; 
  folderQuery += " 
  
  
  "; 
  
  folderQuery += " 
   
   
   
  "; 
  
  folderQuery += " 
   
   
  "; 
  
  folderQuery += " 
   
   
   
 "; 
 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位置,下面是一个例子:

   
      ...
     
    

AdPane=Off*... 
     
    
          
    
      http://law15.oe.hotmail.com/... 
     
              
    
      http://law15.oe.hotmail.com/... 
     
    
          
    
      http://law15.oe.hotmail.com/... 
     
    
          
    
      http://law15.oe.hotmail.com/... 
     
    
          
    
      http://law15.oe.hotmail.com/... 
     
    
          
    
      http://law15.oe.hotmail.com/... 
     
    
          ... 
    
       
    
   
     
   
  
  
 在本文的控制台示例程序中,我们感兴趣的两个文件夹是收件箱和发件箱的文件夹,它们分别用于接收和发送邮件。 
 在C#环境中解析XML的方法很多,由于我们肯定代码涉及的所有XML文档总是合法的,所以可以利用System.XML.XmlTextReader速度快的优势。XmlTextReader是一个“只向前”的读取器,下面把XML字符数据转换成字符流,初始化XML读取器: 
// 初始化 
  inboxUrl_ = null; 
  sendUrl_ = null; 
  // 装入XML 
  StringReader 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") 
      { 
        // 保存收件箱URL 
        xml.Read(); 
        inboxUrl_ = xml.Value; 
      } 
      // 该节点代表发件箱? 
      if(name == "hm:sendmsg") 
      { 
        // 保存发件箱URL 
        xml.Read(); 
        sendUrl_ = xml.Value; 
      } 
    } 
  }


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


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

// 初始化 
  ArrayList mailItems = new ArrayList(); 
 为获得邮件主题、收件人地址、发件人地址之类的邮件基本信息,我们要用到下面XML格式的WebDAV查询: 

  
  
  
    
   
   
      
    
   
      
    
   
      
    
   
      
    
   
      
    
   
      
    
   
      
    
   
      
    
   
    
   
  
  
  
 生成上述XML查询字符串的C#代码: 
// 构造查询 
  string getMailQuery = null; 
  getMailQuery += " 
  
  
  "; 
  
  getMailQuery += " 
   
   
   
   
  "; 
  
  getMailQuery += " 
   
   
 "; 
 就象前面获取信箱文件夹清单的方式一样,上述请求也通过XMLHTTP用PROPFIND方法发送,这次我们把请求的正文设置成查询字符串。由于当前会话的用户身份已经通过验证,所以XMLHTTP open()调用中不必再提供用户名字和密码: 
// 获取邮件信息 
  xmlHttp_.open("PROPFIND", folderUrl, false, null, null); 
  xmlHttp_.send(getMailQuery); 
  string folderInfo = xmlHttp_.responseText;


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

   
     
        http://sea1.oe.hotmail.com/cgi-bin/hmdata/...      
     
   

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

1 
      
     
          
      
     
          
     
       Mark Anderson 
      
     
          
     
       RE: New Information 
      
     
          
     
       2002-08-06T16:38:39 
      
     
          
     
       1238 
      
     
        
     
    
        
    
      HTTP/1.1 200 OK 
     
    
      
    
   
    
   
  
    ... 
  
 观察服务器返回的应答,我们发现每一个节点包含一组标识邮件的域,例如通过标记可提取出邮件。下面我们再次使用System.XML.XmlTextReader解析这个XML数据流,首先初始化流读取器: 
  
MailItem mailItem = null; 
  
  // 装入XML 
  
  StringReader 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) 
        { 
          // 创建一个新的MailItem 
          mailItem = new MailItem(); 
        } 
        // 结束? 
        if(nodeType == XmlNodeType.EndElement) 
        { 
          // 保存email 
          mailItems.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实例作为参数,返回邮件的内容: 
///  

  /// 下载MailItem指定的邮件 
  /// 
  public string LoadMail(MailItem mailItem) 

  { 

    // 邮件的URL 

    string 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()方法所示。 

/// 

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

HotmailCSharp_code.zip(8K)。 )

标签:XML,xml,Hotmail,string,C#,访问,null,邮件
From: https://blog.51cto.com/u_3319687/6603697

相关文章

  • 使用C#发送邮件
    最近有用户提出了一个新的需求,希望公司的ERP系统在交易申请书被批准以后自动发邮件到相关人员的邮箱中,让他们能第一时间知道。因为他们不想多打一次电话,也不愿意每天都开着ERP,但是他们的Outlook能每5分钟自动扫描一次新邮件跳出提醒。用户的需求并不过分,所以我就接下来了。在VS2003......
  • [MEF]第05篇 MEF的目录(Catalog)筛选
    一、演示概述本示例演示如何使用MEF提供的目录(Catalog)的扩展机制实现可过滤导出部件的自定义目录类。主要是通过继承ComposablePartCatalog基类,并实现接口INotifyComposablePartCatalogChanged来完成的。相关下载(屏幕录像):http://yunpan.cn/cVkvuUNfuDtTX 访问密码567d......
  • 光脚丫学ASP.NET MVC(0001):创建ASP.NET MVC应用程序项目
    演示视频:http://u.115.com/file/cloa5hpg本演示介绍了如何使用VisualStudio2010创建ASP.NETMVC应用程序项目。下面是VS的新建项目对话框。其中红色线圈住的两个模板是VS2010安装时就自带的ASP.NETMVC2的项目模板,可以从官方网站下载最新的项目模板。下图是新建单元测试项目对话......
  • [WPF]静态资源(StaticResource)和动态资源(DynamicResource)
    一、文章概述本演示介绍了WPF的静态资源和动态资源的基本使用,并对两者做了简单的比较。静态资源(StaticResource)指的是在程序载入内存时对资源的一次性使用,之后就不再访问这个资源了;动态资源(DynamicResource)使用指的是在程序运行过程中然会去访问资源。相关下载(代码、屏幕录像):h......
  • 03-Vue.js环境准备-使用vue-cli快速搭建项目(cli3+)
    一、文章大纲二、安装环境本文基于如下的环境进行试验的:Windows10中文64位专业版。v12.18.3版本的Node.js。@vue/cli4.5.4的Vue.js和cli。三、安装vue-cli使用npm全局安装vue-cli:npminstall-g@vue/cli可以使用如下的创建项目的命令,查看vuecli的安装情况:按照上面的提示,先卸载......
  • array_reduce的使用
    当使用array_reduce函数编写博客时,可以使用它来对一个数组进行迭代并将每个元素归约(规约)成一个单一的值。下面是一个简单的示例来说明它的用法://假设我们有一个博客数组,每个博客都有一个评论数$blogs=[['title'=>'博客1','comments'=>10],['title'=>'博客......
  • python: objct property
     """clerker.py类edit:geovindu,GeovinDudate:20230672IDE:PyCharm2023.1.2clerker.__dict__窥探私有属性私用属性clerker._Clerker.__age=-1clerker.__age=-1"""importsysimportosclassClerker(object):"""......
  • pytorch的学习
    三种编译方式的优缺点 Pytroch中的加载数据主要涉及了两个类,一个叫Dataset,一个叫Dataloader.举一个不恰当的例子,我们要在诸多的垃圾(数据)中找到我们所需要的垃圾(数据),Dataset就是将其中的可回收垃圾提取出来,并且将它们进行编号,同时可以根据编号获取相对应的垃圾,同时获取相......
  • 1.4 编写简易ShellCode弹窗
    在前面的章节中相信读者已经学会了使用Metasploit工具生成自己的ShellCode代码片段了,本章将继续深入探索关于ShellCode的相关知识体系,ShellCode通常是指一个原始的可执行代码的有效载荷,攻击者通常会使用这段代码来获得被攻陷系统上的交互Shell的访问权限,而现在用于描述一段自包含......
  • 菜鸟学习Spring——SpringMVC注解版前台向后台传值的两种方式
    一、概述。在很多企业的开法中常常用到SpringMVC+Spring+Hibernate(mybatis)这样的架构,SpringMVC相当于Struts是页面到Contorller直接的交互的框架也是界面把信息传输到Contorller层的一种架构,通过这个架构可以让我们把页面和Contorller层解耦,使得开发人员的分工更加明确。二、代码演......