和雅虎中国亲密接触

来源:互联网 发布:大数据与房地产公司 编辑:程序博客网 时间:2024/04/29 19:22

BQYAHOO在替我们做什么

 

从本质上来说,BQYAHOO只是代替你做一些你也采取其他方式来做的事情。比如代替你登陆邮箱,代替你下载附件。那么我们来看看,没有BQYAHOO的帮助,我们该怎么做这些事情。

 

第一步:我们首先来到邮件登录网页,输入自己的用户名和密码,最后单击登录。

第二步:进入了邮箱,但是没有进入收件箱,这时看不到邮件也下载不了附件,只好在单击收件箱的链接。

第三步:进入了收件箱,这个时候看到了邮件,如果你想下载附件,那么你还需要进入特定的邮件,然后单击相关链接下载附件。但是,对于BQYAHOO来说,一切到此结束。现在这个年代,即使是程序也要讲究智能,其实收件箱网页的源代码已经将所有信息一览无余的告诉了你。BQYAHOO会对这些信息进行分析,最终计算出附件的下载地址。

接下来将结合程序讲解如何在BQYAHOO中实现登陆邮箱和下载附件。

 

 

 

 

选一把顺手的剑

 

在编写BQYAHOO的时候,我总是在类库的选择上费尽心机。有时候会出现彼此矛盾的情况,如果你使用A类库,那么就不可以同时使用B类库。总的来说,应该尽可能选择由一家公司提供的类库,比如说微软。

 

在登录邮件和下载附件的过程当中,你不得不使用HTTP函数库,因为和雅虎中国服务器的交互就是建立在HTTP协议上的。当然你也可以使用Windows Sockets封装一个自己的HTTP函数库,不要以为我在开玩笑,我差一点就这么做了(在文章的后面会提到我为什么这么做)。

 

目前有多种HTTP函数库可供选择,仅考虑微软提供的函数库就有以下四种:

l        MFC HTTP

l        ATL Server HTTP

l        Windows HTTP

l        Windows Internet

 

BQYAHOO里面使用的是最后一种:Windows Internet 。它在这些函数库里面是封装程度最低的,但是,我却觉得用起来最为顺手,所需要知道的额外信息很少。关于Windows Internet函数库的详细内容,请你阅读MSDN,这里只介绍源代码中使用到的函数及其相关知识。

 

为了使用Windows Internet函数库,你必须在程序中使用头文件wininet.h和库文件wininet.lib

 

其实在BQYAHOO当中,一共只使用到这个函数库当中的三个函数:

l        InternetOpen

l        InternetOpenUrl

l        InternetReadFile

怎么样?是不是觉得很轻爽?

 

///这里可以对上面三个函数的用法进行简单介绍,视情况而定。/////

 

 

 

 

阿里巴巴,快开门

 

如果我告诉你,在IE中不用输入用户名和密码,只需根据用户名,密码以及登录网页上的其它信息计算出一个网址,然后输入到IE的地址栏里面,就可以正常登录雅虎邮箱,你觉得可能吗?

 

其实你自己可以尝试一下,先输入用户名和密码(故意输入错误的信息),然后单击登录,最后,你会在地址栏上看到一串很长的网址。如果你输入正确的用户名和密码,就看不到这个网址,因为IE会自动跳转到邮箱当中,那么你看到的就是邮箱的网址。

 

可以通过下面源码获得该网页的源码。

bool WebClient::BeforeLogIn()//进入登陆页面

{

    hsession=::InternetOpen("biqiong",INTERNET_OPEN_TYPE_PRECONFIG ,NULL ,NULL,NULL);

    hconnect=::InternetOpenUrl(hsession,BeforLogInUrl.c_str (),NULL,NULL,CONNECT_FLAG,NULL);

 

    GetResponse();

    GetHead(); 

   

    xlog.log ("进入登陆页面");//记录日子信息

    xlog.log (head);

    xlog.log (response);

   

    if(TestResponse()==false) return false;

    if(strstr(response,"ShowFolder?rb=Inbox" )!=NULL) MayBeIn=true;//目前已经登陆成功

 

    return true;

 

}

现在问题的关键是如何计算出这个网址?它如何产生?需要知道哪些信息?

 

在登录的时候,雅虎中国会使用MD5加密用户名和密码,密文会在服务器端进行验证,最终确定你的身份。以下雅虎中国的网页源代码或许会给你一些启示(... ... ...表示删减)。

<script language=javascript>

/*

 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message

 * Digest Algorithm, as defined in RFC 1321.

 * Copyright (C) Paul Johnston 1999 - 2000.

 * Updated by Greg Holt 2000 - 2001.

 * See http://pajhome.org.uk/site/legal.html for details.

 */

var hex_chr = "0123456789abcdef";

function rhex(num)

{

 str = "";

 for(j = 0; j <= 3; j++)

 str += hex_chr.charAt((num >> (j * 8 + 4)) & 0x0F) +

 hex_chr.charAt((num >> (j * 8)) & 0x0F);

 return str;

}

function str2blks_MD5(str)

{

 nblk = ((str.length + 8) >> 6) + 1;

 blks = new Array(nblk * 16);

 for(i = 0; i < nblk * 16; i++) blks[i] = 0;

 for(i = 0; i < str.length; i++)

 blks[i >> 2] |= str.charCodeAt(i) << ((i % 4) * 8);

 blks[i >> 2] |= 0x80 << ((i % 4) * 8);

 blks[nblk * 16 - 2] = str.length * 8;

 return blks;

}

function add(x, y)

{

 var lsw = (x & 0xFFFF) + (y & 0xFFFF);

 var msw = (x >> 16) + (y >> 16) + (lsw >> 16);

 return (msw << 16) | (lsw & 0xFFFF);

}

function rol(num, cnt)

{

 return (num << cnt) | (num >>> (32 - cnt));

}

... ... ...

function ii(a, b, c, d, x, s, t)

{

 return cmn(c ^ (b | (~d)), a, b, x, s, t);

}

function MD5(str)

{

 x = str2blks_MD5(str);

 var a = 1732584193;

 var b = -271733879;

 var c = -1732584194;

 var d = 271733878;

 

 for(i = 0; i < x.length; i += 16)

 {

 var olda = a;

 var oldb = b;

 var oldc = c;

 var oldd = d;

 a = ff(a, b, c, d, x[i+ 0], 7 , -680876936);

 ... ... ... ...

b = ii(b, c, d, a, x[i+ 9], 21, -343485551);

 a = add(a, olda);

 b = add(b, oldb);

 c = add(c, oldc);

 d = add(d, oldd);

 }

 return rhex(a) + rhex(b) + rhex(c) + rhex(d);

}

function hash(form,login_url) {

 var url;

 if (arguments.length > 1 && login_url != "") { // in case login_url is not passed in

 url = login_url;

 } else {

 url = "http://edit.bjs.yahoo.com/config/login";

 }

 url += "?";

 if (navigator.userAgent.indexOf("Mozilla/4")==0) {

 var passwd = form.passwd.value;

 var hash1 = MD5(form.passwd.value);

 var challenge = form[".challenge"].value;

 var hash2 = MD5(form.passwd.value) + challenge;

 var hash;

 if(form.passwd.value){

 hash=MD5(hash2);

 } else {

 hash="";

 }

 var js = 0;

 for(i=0; i<form.elements.length; i++){

 if(form.elements[i].name.length <=0) {

 continue;

 }

 if(i > 0){

 url += "&";

 }

 url += form.elements[i].name;

 url += "=";

 if(form.elements[i].name == "passwd"){

 url += hash;

 } else if (form.elements[i].type == "checkbox" && !form.elements[i].checked) {

 url += "";

 } else if (form.elements[i].name == ".save"){

 url += "1";

 } else if (form.elements[i].name == ".js"){

 js = 1;

 url += "1";

 } else {

 url += escape(form.elements[i].value);

 }

 }

 url += "&.hash=1";

 if(js == 0){

 url += "&.js=1";

 }

 url += "&.md5=1";

 location.href=url;

 form.onsubmit=null;

 return false;

 }

 return true;

}

</script>

 

雅虎中国使用javaScript实现了MD5加密,而我们要做的就是在程序中也实现上面的过程并产生同样的结果。

 

///这里可以对MD5加密进行简单介绍,视情况而定。/////

 

在计算的过程当中,我们除了需要知道用户名和密码,而且还需要知道当前网页上的challenge值。每次打开登录网页,该值都发生随机变化,这也是安全的保证。以下代码可以告诉你该值的作用。

var challenge = form[".challenge"].value;

var hash2 = MD5(form.passwd.value) + challenge;

 

以下代码用C++计算登录网址。

 

const char * CGLIU::GetLogInUrl()

{

        //以下代码计算登录URL,来自于开源项目YPOPs!

        //下面的MD5加密完全再现了雅虎中国网页上的加密过程 

        char hash[256];

       

        MD5_CTX ctx;

        MD5_Init(&ctx);

        MD5_Update(&ctx, password, strlen(password));

        MD5_Final(reinterpret_cast<unsigned char *>(hash), &ctx);

       

        hash[0] = '/0';

        rhex(ctx.A, data);

        strcat(hash, data);

        rhex(ctx.B, data);

        strcat(hash, data);

        rhex(ctx.C, data);

        strcat(hash, data);

        rhex(ctx.D, data);

        strcat(hash, data);

        strcat(hash, ChallengeText);

 

        MD5_Init(&ctx);

        MD5_Update(&ctx, hash, strlen(hash));

        MD5_Final(reinterpret_cast<unsigned char *>(hash), &ctx);

       

        hash[0] = '/0';

        rhex(ctx.A, data);

        strcat(hash, data);

        rhex(ctx.B, data);

        strcat(hash, data);

        rhex(ctx.C, data);

        strcat(hash, data);

        rhex(ctx.D, data);

        strcat(hash, data);

 

        sprintf(data,          "%s?login=%s&passwd=%s&.save=1&.intl=cn&.src=ym&.challenge=%s&.hash=1&.js=1&.md5=1",   HTTP_LOGIN, user, hash, ChallengeText);

        strcpy(LogInUrl,data);

        return (const char *)LogInUrl;

 

}

 

inline void rhex(unsigned int num, char *data)

{

    CStdString hex_chr = "0123456789abcdef";

    CStdString str;

    for(int j = 0; j <= 3; j++)

    {

        str += hex_chr[(num >> (j * 8 + 4)) & 0x0F];

        str += hex_chr[(num >> (j * 8)) & 0x0F];

    }  

    strcpy(data, str);

}

 

函数GetLogInUrl()返回登录网址,使用它我们就可以登录雅虎邮箱了。

 

 

 

冲锋前进

 

在上面我们提到如果输入正确的用户名和密码,IE会自动地跳转到邮箱。在程序当中也是如此,当我们向雅虎中国服务器发送正确的登录网址,接下来,我们将接受到进入邮箱后的网页内容。

到目前为止,我们终于登录成功并进入邮箱。接下来的要做的就是进入收件箱。

bool WebClient::InInBox(const string MailHostUrl)

{

   

    string InBoxUrl;

    InBoxUrl=MailHostUrl+"ym/ShowFolder?rb=Inbox&box=Inbox";

 

    hconnect=::InternetOpenUrl(hsession,InBoxUrl.c_str (),NULL,NULL,CONNECT_FLAG,NULL);

 

    GetResponse();

    GetHead(); 

 

    xlog.log ("进入收件箱");//记录日子信息

    xlog.log (head);

    xlog.log (response);

 

 

    if(TestResponse()==false) return false;

   

    return true;

}

 

 

目的地

 

收件箱就是我们的目的地,来到这一页,BQYAHOO就可以通过网页分析得到所需要的所有信息,最终获得所有的邮件附件的下载网址。

 

接下来就是下载附件了。

bool WebClient::DownLoadFile(string FileSaveName,string DownLoadUrl)

{

    //所有下载操作,最终由以下代码执行。

    //虽然简单粗俗,却很是实用。

    const unsigned long CacheSize=100*1024;//分块下载,每一块100K

    HINTERNET hfile;

    char cache[CacheSize];

    unsigned long byteofread=CacheSize;

    FILE *fdest;

 

    fdest=fopen(FileSaveName.c_str (),"wb");  

    hfile=::InternetOpenUrl(hsession ,DownLoadUrl.c_str (),NULL,NULL,NULL,NULL);   

    while(::InternetReadFile(hfile,cache,CacheSize,&byteofread)==true)

    {              

        fwrite(cache ,sizeof(char),byteofread ,fdest);

        if(byteofread==0)break;

    }  

    fclose(fdest); 

    return true;

}

 

在下载这个地方,我花费了很多精力。我一直想实现而从来就没有实现的目标是:多线程下载。我对于将一个文件分块,然后使用多线程下载没有兴趣。因为BQYAHOO每个附件的内容本身就不多,不可以超过10M,而且雅虎中国的网速也不差,没有必要这么做。对于利用多线程同时下载多个文件,我期待已久。我尝试了各种多线程库和HTTP函数库,甚至用Windows Sockets封装一个自己的HTTP函数库,但这些似乎都没有什么作用。

 

每当我在同一时间向不同的附件网址发起请求时,只有一个请求会得到答复,而其他请求之能够等待。从线程的角度来看,那个时间里确实存在着多线程。也就是说即便使用多线程,文件还是一个接一个的被下载。而且使用多线程会带来不稳定,排在后面的线程常常“无疾而终”,什么也没有下载。

 

总结

 

总的来说,BQYAHOO相当于一个功能极其简陋,作用极其明确的”IE”。在和雅虎中国服务器的交互过程当中,我们只是做了两件事情:获取网页和获取文件。整个登录过程当中,难点就是计算登录网址,如果你熟悉JavaScript,也不是那么困难,最多就是照葫芦画瓢。

 

其实在本文当中,有很多关键内容我都没有讲到,比如说如何得到challenge。这些内容我都留到下一篇文章讲解,因为它们统统都属于网页分析。 

 

原创粉丝点击