<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[大眼夹的鸟巢]]></title>
  <link href="http://blog.dayanjia.com/atom.xml" rel="self"/>
  <link href="http://blog.dayanjia.com/"/>
  <updated>2012-05-16T02:40:38+08:00</updated>
  <id>http://blog.dayanjia.com/</id>
  <author>
    <name><![CDATA[Clippit]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[Dropquest 2012 详尽完全攻略解析]]></title>
    <link href="http://blog.dayanjia.com/2012/05/dropquest-2012-walkthrough/"/>
    <updated>2012-05-14T13:05:00+08:00</updated>
    <id>http://blog.dayanjia.com/2012/05/dropquest-2012-walkthrough</id>
    <content type="html"><![CDATA[<p><a href="http://img.dayanjia.com/di/QUA5/dropquestbox%20%282%29.png"><img class="right" src="http://img.dayanjia.com/dt/QUA5/dropquestbox%20%282%29.png"></a></p>

<p>一年一度的Dropbox解密游戏又开始了，这次的解密活动从5月13日开始，到6月2日结束。在此期间完成整个解密游戏的用户都将获得至少1GB的存储空间，最先完成的玩家还有特别的奖品。当然，截止此时，完成者已经有很多了。不过，享受解谜、挑战智商的乐趣才是最重要的。越到后期，你会越感到谜题设计之精巧，令人不得不叹服，因此完整这份攻略也不是一件容易的事。本次DropQuest 2012的<a href="https://www.dropbox.com/dropquest2012">参加入口在这里</a>。需要注意的是，有些解密的具体内容不同，但是方法都是类似的。</p>

<!--more-->


<h2>序</h2>

<p>说实话，建造一台时光机器是一个很冲动的想法，但我发誓这一切都仅仅源自好奇心。当我今天早上带上你的时候，我绝非想拯救恐龙世界或是知晓彩票中奖号码什么的。我只是想在历史的进程中做一个置身事外的观察者而已——是的，就是那些我们在教科书上读到的故事。至于访问未来，我想我会控制住自己不去做这样的冒险……</p>

<p>好吧，我的自控能力真的很糟糕。这一切都发生的那么突然！在跟爱因斯坦进行了一次发人深思的交谈后（他的确是个闹腾的家伙），我的脑中充满了对未来科学的幻想，于是我将年份拨到了未来（是的，我知道你警告过我）。我对接下来会发生的事完全没有准备。最终，在机器能量即将耗尽的时候，我们坠毁在一片不毛之地，眼前根本没有未来飞行的汽车或是宇宙飞船。</p>

<p>我们搁浅在了2054年，时间机器也坏了。唯一让我感到欣慰的是，我们还在一起！</p>

<h2>第一关</h2>

<p>首先我需要找到机载电脑的诊断记录。许多部分都被烧毁了，不过我相信它们是可以被修复的。这里是未来，不是吗？找到一个量子陀螺仪能有多困难？它们可能已经能长在树上了！</p>

<p>这些都还简单，除了我忘记了我那5位数字的密码。我应该用1Password这样的软件的，不过我还是想到了一些线索。</p>

<blockquote><p>1. 前两个数字的乘积是24<br/>2. 第四个数字是第二个数字的一半<br/>3. 最后两个数字的和与第1、3个数字的和相同<br/>4. 所有数字的和是26<br/>5. 第二个数字比最后一个数字大</p></blockquote>


<p>这个很简单，其实就是一个多元方程组，相信上过初中的都知道。如果你不愿意自己算的话，可以交给数学软件去解。WolframAlpha是一个很棒的在线服务，<a href="http://www.wolframalpha.com/input/?i=a*b%3D24%2Cd%3Db%2F2%2Cd%2Be%3Da%2Bc%2Ca%2Bb%2Bc%2Bd%2Be%3D26%2Cb%3Ee">输入方程后可以得到结果</a>。这虽然是个不定方程，但是符合剧情的整数解只有一个。密码是<em>38645</em>，填入页面后提交即可。这一关可能会出现多种不同的条件，所得答案也不一样。</p>

<h2>第二关</h2>

<p>随后，诊断记录会添加进Dropbox文件夹下。整个Dropquest需要的文件都会放在<code>Dropquest 2012</code>这个目录下。打开<code>38645.txt</code>文件，其中包含了损坏部件的列表。</p>

<blockquote><p>如需替换核心部件，请联系工程团队(<a href="http://www.dropbox.com/about">http://www.dropbox.com/about</a>)获取更多信息。</p><p>1. 某个名字为6个字母，其中有两个大写的人<br/>2. 某个姓仅仅用首字母标出的人<br/>3. 某个招聘人员<br/>4. 某个戴眼镜的人<br/>5. 某个隔壁是个动物的人</p><footer><strong>38645.txt</strong></footer></blockquote>


<p>我们可以在Dropbox的团队人员列表中找到符合条件的人：</p>

<ol>
<li>ChenLi Wang</li>
<li>Ryan M., Gautam J., Todd E., Chris V., Lars Fjeldsoe-N., Ramsey H</li>
<li>Alison Davis, Donald James, Karen Sperling, V, Allison Louie</li>
<li>Michael Nagy, Will Stockwell, Martin Baker, Ben Darnell, David Stein, Marcus Colins, Alicia Chen, Sean Li, Naveen Agrawal</li>
<li>Emily Zhao</li>
</ol>


<p><a href="http://img.dayanjia.com/di/4OIJ/About%20Dropbox.jpg"><img class="right" src="http://img.dayanjia.com/dm/4OIJ/About%20Dropbox.jpg"></a></p>

<p>鼠标移向ChenLi Wang的介绍，他说如果需要修复时间机器，你需要把“Knight”放在合适的位置移动。这里的Knight是国际象棋的“马♞”。根据国际象棋的规则，马是走“日”字的。把团队列表想象成一个国际象棋棋盘后，从ChenLi Wang开始，找到各个符合条件的人名，路径为ChenLi, Ramsey, Allison, Naveen, Emily（如右图）。最后，将鼠标放在Emily Zhao的介绍上，她说把之前路径的人名首字母连起来作为password，然后进入<code>dropbox.com/dropquest2012/password</code>。如法炮制后打开https://www.dropbox.com/dropquest2012/crane即可。</p>

<h2>第三关</h2>

<p>哇，Emily果然给我们回应了！</p>

<blockquote><p>你可以开始自动修复了，但是你需要一个密码来启动。</p><p>我把密码加密过了——我不能告诉你太多，否则你会很危险的。小心。</p><footer><strong>Emily</strong></footer></blockquote>


<p>打开Dropquest 2012下的<code>Instructions from Emily</code>目录，里面是7个图标。说的真可怕，不过这些图标似乎似曾相识。</p>

<p>事实上这是Dropbox网页版上的图标，根据这些图标的文字说明：</p>

<ol>
<li>Sharing</li>
<li>Move</li>
<li>Upload</li>
<li>Download</li>
<li>Get Started</li>
<li>Show Deleted Files</li>
</ol>


<p>再次将首字母连起来，将密码<em>smudges</em>输入。</p>

<h2>第四关</h2>

<p>看上去已经开始修复了，希望这意味着只要找到缺失的部件，就可以修好它，然后把我们带到过去（不就是“现在”吗？）。我们环顾四周，不知道发生了什么，也没有找到遗失的部件。在地平线的尽头，无数摩天大楼若隐若现，这一切就像泛滥成灾的植物一样。</p>

<p>在我们周围，高塔林立，让人觉得这里的夜晚就像白天一样明亮。奇怪的是，如此一个大都市却非常安静。我不知道这意味着什么，不过至少不会有炸弹或机器人军队把我们赶尽杀绝，也许吧。这里还有幸存者吗？Emily的信息如此神秘，或许这里还有其他人……</p>

<p>在一个角落里，我发现了一个闪着光的东西。哦那是……我上个月的备忘录？也许这是个诡异的玩笑，但是既然毫无头绪，那就看看到底去过哪里。</p>

<p>使用Google Maps来看看每日的行程都在什么地方。</p>

<h3>星期一</h3>

<ol>
<li>在Lafayette公园网球场打网球</li>
<li>到California Pacific Medical Center体检</li>
<li>到Park Branch的公共图书馆还书</li>
<li>去The Mint唱卡拉OK</li>
<li>在24街的Happy Donuts吃夜宵</li>
<li>回Midtown Terrace</li>
</ol>


<iframe width="425" height="350" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="http://maps.google.com/maps/ms?msa=0&amp;msid=211054947841122726328.0004bff99638f4f5615ba&amp;hl=zh_CN&amp;ie=UTF8&amp;t=v&amp;iwloc=0004bff9c82c29205dff9&amp;ll=37.770787,-122.440526&amp;spn=0.0411,0.027123&amp;output=embed"></iframe>


<br /><small>在较大的地图中查看<a href="http://maps.google.com/maps/ms?msa=0&amp;msid=211054947841122726328.0004bff99638f4f5615ba&amp;hl=zh_CN&amp;ie=UTF8&amp;t=v&amp;iwloc=0004bff9c82c29205dff9&amp;ll=37.770787,-122.440526&amp;spn=0.0411,0.027123&amp;source=embed" style="color:#0000FF;text-align:left">Dropquest 2012 Chapter 4 Monday</a></small>


<h3>星期二</h3>

<ol>
<li>从24街BART车站出发</li>
<li>徒步到Twin Peaks</li>
<li>去Geary的百思买买点东西</li>
<li>去宾利的店里看看车</li>
<li>去24街的El Farolito Taqueria吃东西</li>
</ol>


<iframe width="425" height="350" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="http://maps.google.com/maps/ms?msa=0&amp;msid=211054947841122726328.0004bff9d2905675da9c6&amp;hl=zh_CN&amp;ie=UTF8&amp;t=v&amp;ll=37.768005,-122.428968&amp;spn=0.031736,0.035674&amp;output=embed"></iframe>


<br /><small>在较大的地图中查看<a href="http://maps.google.com/maps/ms?msa=0&amp;msid=211054947841122726328.0004bff9d2905675da9c6&amp;hl=zh_CN&amp;ie=UTF8&amp;t=v&amp;ll=37.768005,-122.428968&amp;spn=0.031736,0.035674&amp;source=embed" style="color:#0000FF;text-align:left">Dropquest 2012 Chapter 4 Tuesday</a></small>


<h3>星期四</h3>

<ol>
<li>从旧金山动物园出发</li>
<li>在Great Highway旅馆小憩</li>
<li>骑自行车去37大道</li>
<li>在加州莎士比亚剧场夏季音乐学院看哈姆雷特</li>
<li>在Sunset水库散步</li>
<li>去Irving街的Sunset超市买点水果</li>
<li>在Sigmund Stern Grove散步</li>
</ol>


<iframe width="425" height="350" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="http://maps.google.com/maps/ms?msa=0&amp;msid=211054947841122726328.0004bff9eeef2a305feb9&amp;hl=zh_CN&amp;ie=UTF8&amp;t=v&amp;ll=37.748594,-122.493784&amp;spn=0.02984,0.031805&amp;output=embed"></iframe>


<br /><small>在较大的地图中查看<a href="http://maps.google.com/maps/ms?msa=0&amp;msid=211054947841122726328.0004bff9eeef2a305feb9&amp;hl=zh_CN&amp;ie=UTF8&amp;t=v&amp;ll=37.748594,-122.493784&amp;spn=0.02984,0.031805&amp;source=embed" style="color:#0000FF;text-align:left">Dropquest 2012 Chapter 4 Thursday</a></small>


<h3>星期六</h3>

<ol>
<li>在Thai Thai Noodle吃点东西</li>
<li>去Fiddler&#8217;s Green见朋友</li>
<li>去Glamour Closet买衣服</li>
<li>去Piazza Pelligrini订餐</li>
<li>去Searchlight市场买饮料</li>
</ol>


<iframe width="425" height="350" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="http://maps.google.com/maps/ms?msa=0&amp;msid=211054947841122726328.0004bffa172875dffe829&amp;hl=zh_CN&amp;ie=UTF8&amp;ll=37.798778,-122.412012&amp;spn=0.015605,0.014931&amp;t=v&amp;output=embed"></iframe>


<br /><small>在较大的地图中查看<a href="http://maps.google.com/maps/ms?msa=0&amp;msid=211054947841122726328.0004bffa172875dffe829&amp;hl=zh_CN&amp;ie=UTF8&amp;ll=37.798778,-122.412012&amp;spn=0.015605,0.014931&amp;t=v&amp;source=embed" style="color:#0000FF;text-align:left">Dropquest 2012 Chapter 4 Saturday</a></small>


<p>可以看出，每天的轨迹都是一个英文字母。因为Dropbox的总部在旧金山，所以所有的行程都在那个城市里。最后把四个字母组合起来<em>SOMA</em>便是本关的密码。</p>

<h2>第五关</h2>

<p>在追踪了我的路线后，我得到了SOMA（市场南街区），那里正是当时Dropbox的总部所在。许多全息显示屏点缀着街道，但是许多没电了。或许我可以用时间机器里的电力重新让它们恢复工作，说不定能有更多线索，就像刚才的行程表一样。我只需要把线缆和电源插座对应起来，但是这些符号是什么意思呢？</p>

<p><img src="http://img.dayanjia.com/di/V73N/symbols.png"></p>

<p>这些符号都是电源插座的形象，大家应该对倒数第二个比较熟悉，这个是中国使用的插座。在<a href="http://electricaloutlet.org/electricaloutlettable">ElectricalOutlet.org</a>可以查到这些插座的类型，图示中的插座分别是<a href="http://electricaloutlet.org/type-m">M型</a>、<a href="http://electricaloutlet.org/type-a">A型</a>、<a href="http://electricaloutlet.org/type-d">D型</a>、<a href="http://electricaloutlet.org/type-l">L型</a>、<a href="http://electricaloutlet.org/type-i">I型</a>、<a href="http://electricaloutlet.org/type-b">B型</a>。</p>

<h2>第六关</h2>

<p>哇！果然是这样！全息显示屏开始显示一些看上去像是日历或者事件的东西。为什么颜色如此混乱？难道电线有问题？</p>

<p>这一关是和Dropbox事件日志有关，不同人可能会得到不同的图片。我的图片是这样的：</p>

<p><img src="http://img.dayanjia.com/di/VINZ/f84h732.png"></p>

<p>可以看到色块的颜色和上面的文字描述并不相符，看上去很诡异。我们需要将图片的颜色反相，大多数图像处理软件都有这个功能。当然也可以用在线工具，例如<a href="http://www.converthub.com/invert-colors/">ConvertHub</a>，只需要输入图片URL就可以了。上图反相后的结果是这样的：</p>

<p><img src="http://img.dayanjia.com/di/BQH0/f84h732-inverted.png"></p>

<p>此时，便有正确的描述和色块了，但是依然有三个色块不正确，他们是ORANGE、RED和BLUE。这下面的Y01、M11、D28代表了一个日期，进入<a href="https://www.dropbox.com/events">Dropbox日志</a>页并跳转到2001年11月28日。可以看到一条信息：</p>

<blockquote><p>Here is your <a href="http://soundcloud.com/reasonable/for-sure">next clue</a>, but you may also need <a href="https://www.dropbox.com/sh/hfuiyjr0g369r6t/FLRZ9t-7Bq/legend.png">this</a>.</p></blockquote>


<h2>第七关</h2>

<p>第一个链接指向SoundCloud上面的一段音乐，第二个链接的内容是一些数学符号。</p>

<p>SoundCloud上的那首歌貌似没什么关系，但是在实时评论中出现了一个叫做@dropquest2012的帐号。当然，现在那段音轨的评论实在太多了，不容易直接发现，可以进入<a href="http://soundcloud.com/dropquest2012">@dropquest2012的页面</a>来看它发表过的评论。</p>

<p><img src="http://img.dayanjia.com/di/QMJL/legend.png"></p>

<p>这里又会出现不同的情形，需要根据给出的图片得到答案。显然图片中的值对应了评论的时间。在上图中，这些值写成保留两位小数的形式便是1.57、3.14、1.14、2.30、0.37、0.32，最后一个不知道是什么意思，不过不妨碍解谜。这些值可以简单地按计算器得到，也可以在Google搜索框中计算。再去看@dropquest2012的评论，得到对应的字母为<em>LEADING</em>。于是<code>dropbox.com/dropquest2012/LEADING</code>便是结果链接。</p>

<h2>第八关</h2>

<p>修复完这些全息电脑，获得了一些线索后，我们听到一些声音……来自于不知名的某人。</p>

<blockquote><p>你们好，</p><p>我可以想象这些未来的事情让你很震惊，但是所有一切很快就会变得有意义。有人在监视我们，所以我只能告诉你密码。我知道你需要哪些部件，而且能告诉你第一块在哪里。相信我。</p><p>这条线索只有来自你们时间的人才能理解。<img src="http://img.dayanjia.com/di/4Y42/mashup.png"></p></blockquote>


<p>我甚至不知道这个家伙怎么会知道我们所处的困境的。但是他既然愿意免费给我一块部件，那应该是个好人……是吧？</p>

<p>这一关的谜题具有很强的地域文化性。图片中的第一行文字说道“Silly Costume”，第二行看似是无关的字母，不过仔细观察可以发现是美国一些著名大学的缩写。这些大学都有所谓的“吉祥物”，往往由一个人穿着相应形象的衣服扮演，会出现在诸如体育比赛等活动中，所以被称作“愚蠢的服装”。这一特有的文化现象是国内的高校所没有的。</p>

<ul>
<li>MIT - 麻省理工学院 - Tim the Beaver</li>
<li>URI - 罗德岛大学 - Rhody the Ram</li>
<li>LMU - 洛约拉马利蒙特大学 - Iggy the Lion</li>
<li>UGA - 乔治亚大学 - Uga</li>
<li>LSU - 路易斯安那州立大学 - Mike the Tiger</li>
<li>GSU - 乔治亚州立大学 - Pounce</li>
<li>UNLV - 内华达大学拉斯维加斯校区 - Hey Reb</li>
<li>UF - 佛罗里达大学 - Albert and Alberta</li>
<li>UNCC - 北卡罗来纳州立大学夏洛特分校 - Norm the Niner</li>
<li>MIT - 麻省理工学院 - Tim the Beaver</li>
</ul>


<p>同样，把所有吉祥物的首字母串起来，得到答案<em>triumphant</em>。</p>

<h2>第九关</h2>

<p>好吧，看上去我们可以相信那个神秘的赞助者……我们的一个超适应电浆坏了，还有一些其他部件需要修复。但是那个帮助者没有了消息。幸运的是，当我们回到机载电脑放入现有的部件后，系统得到了更新。这让我们有了更多的时间。检查一下Dropquest文件夹，我们需要重新把数据排列以进行下一步操作。</p>

<p>在<code>Onboard Computer Update</code>目录中，有很多杂七杂八的文件。我们需要把这些文件按文件扩展名来排序。可惜的是，Windows的资源管理器并没有提供这个功能。在类Unix系统下我们可以使用<code>ls -1X</code>来得到排序后的结果：</p>

<pre><code>your.ini
next.iso
destination.jpg
is.jpg
the.jpg
last.lnk
page.m3u
of.mid
the.pages
tour.pdf
crane.png
but.ppt
might.psd
sixth.rar
tomorrow.rtf
background.tar
sanity.wav
sound.wav
it.wpd
origin.wps
persistence.xls
referral.xml
crazy.z01
cats.zip
</code></pre>

<p>连起来便是<code>your next destination is the last page of the tour...</code>。后面的内容看不懂，但是前面一些单词还是组成了一句话的。来到<a href="https://www.dropbox.com/tour">Dropbox的功能浏览页面</a>，翻到最后一页：</p>

<blockquote><p>嘿，又是我。我有个主意。<a href="https://www.dropbox.com/referrals">邀请我到 Dropbox</a>，这样我们的交流会变得更加方便。我的电子邮件是 savior@dropbox.com。</p></blockquote>


<p>顺水推舟，给savior@dropbox.com发一封邀请。不同人可能会看到不同的邮箱地址。</p>

<h2>第十关</h2>

<p>把帮助者邀请进Dropbox后，他却再次杳无音讯了。你建议我做更多的探索，于是我们又回到了城市中。看着那些曾经天天经过的地方在时间的洗礼下变成了什么样——家园、公司、超市，一切都还是原先的样子，但却被我们不认识的奇怪东西填满了。但我注意到一点，所有的汽车都不见了。我没有看到一辆交通工具。这意味着人们都离开这里的吗？或者已经有一种更先进的旅行方式了？</p>

<p>侦查了一会儿后，我们来到了一家我儿时经常来的博物馆前。40年过去了，这里有一场关于2010年代的展览。这多方便啊！然而我却没有找到任何可用的部件。在展览的中央，有一个奇怪而熟悉的箱子，它被锁上了，表面依稀可见已被腐蚀的字刻。</p>

<p><img src="http://img.dayanjia.com/di/T8VP/23nm34k.png"></p>

<p>似乎有什么东西放在不应该放在的地方……</p>

<p>上面的剧情其实给出了一些提示，不过这一关还是非常令人费解的，而且不同人可能会得到不同的图片，所以答案也会不同。首先，你应当认出上图中的这些缩写都是股票代码。Google提供了<a href="https://www.google.com/finance">财经搜索</a>的服务，可以用来搜索股票代码。</p>

<p>然后就需要做一些联想了——“Something doesn&#8217;t seem to belong though&#8230;”。注意其中有一些通信公司，分别是<a href="http://www.nyse.com/about/listed/tmx.html">TMX</a>（Teléfonos de México）、<a href="http://www.nyse.com/about/listed/ti.html">TI</a>（Telecom Italia S.P.A.）、<a href="http://www.google.com/finance?cid=665536">TKAGY</a>（Telekom Austria AG (ADR)）、<a href="http://www.nyse.com/about/listed/fte.html">FTE</a>（France Telecom）。而这其中又只有墨西哥不是欧洲，所以输入密码<em>Mexico</em>。好吧，我承认这有点牵强，但这的确就是答案。</p>

<p>如果是其他几种图片，会出现TEO（Telecom Argentina S.A.）或是KF（The Korea Fund Inc.），答案就是Argentina或 Korea。</p>

<h2>第十一关</h2>

<p>幸运的是，盒子里放着一个光谱介电放大器（谁把这玩意儿放这儿的？）而且，我们的帮助者（暂且叫他Benny吧）也回来联系我们了！</p>

<blockquote><p>嘿，我得到了你们下一步的方向（也是最后一件需要的部件）。但是你们仍需要做一些解码。</p><p>我使用了在Dropbox发布之前Drew Houston和Arash Ferdowsi试过的一种早期文件加密协议。使用这种方法，文件被相同大小的“块”分割开，每一块使用凯撒密码加密。这种加密方法不是非常安全，你肯定能够破解它！</p><p>UEHVDQADRZWGJXFVFIWEJTWKSRBESAQADRZNXAOWGQTHP</p><footer><strong>Benny</strong></footer></blockquote>


<p>啊，我想Benny在说我们的超特级量子器，蓝宝石Dropbox核心，这将要寻找的下一个目标。</p>

<p><a href="http://zh.wikipedia.org/zh-cn/%E5%87%B1%E6%92%92%E5%AF%86%E7%A2%BC">凯撒密码</a>是一种简单的加密技术，它采用替换加密的方法，明文中的所有字母都按照字母表顺序偏移一定数目就得到了密文。</p>

<p>剧情中说到把密文分成大小相同的块，那么这个45个字符的字符串很可能被分成了5×9块。就这么看字母很难发现什么，写一段小程序来看看每组五个字母之间的相互关系：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="n">cipher</span> <span class="o">=</span> <span class="s">&quot;UEHVD QADRZ WGJXF VFIWE JTWKS RBESA QADRZ NXAOW GQTHP&quot;</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'><span class="k">def</span> <span class="nf">distance</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
</span><span class='line'>    <span class="k">return</span> <span class="p">(</span><span class="nb">ord</span><span class="p">(</span><span class="n">y</span><span class="p">)</span> <span class="o">-</span> <span class="nb">ord</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o">+</span> <span class="mi">26</span><span class="p">)</span> <span class="o">%</span> <span class="mi">26</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'><span class="k">def</span> <span class="nf">print_distance</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
</span><span class='line'>    <span class="k">print</span> <span class="n">distance</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">),</span>
</span><span class='line'>    <span class="k">return</span> <span class="n">b</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'><span class="nb">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">s</span><span class="p">:</span> <span class="nb">reduce</span><span class="p">(</span><span class="n">print_distance</span><span class="p">,</span> <span class="n">s</span><span class="p">),</span> <span class="n">cipher</span><span class="o">.</span><span class="n">split</span><span class="p">())</span>
</span></code></pre></td></tr></table></div></figure>


<p>可以看到输出是<code>10 3 14 8</code>的循环，也就是说，这样的分块是正确的，并且每个分块的都是同一段明文的凯撒密码。原文中提到了“块（Block）”，而恰巧<code>BLOCK</code>这五个字母相互的“距离”也是10、3、14、8。于是便可以猜测真正的明文是<code>BLOCK</code>了。如果没有联想到也没关系，可以把26种可能性全列出来，你会发现只有<code>BLOCK</code>是有意义的单词。那么，每段的凯撒密码偏移值就可以轻松地算出来，分别是：19, 15, 21, 20, 8, 16, 15, 12, 5。将这些数字理解为字母表的索引，得到答案<em>SOUTHPOLE</em>。</p>

<h2>第十二关</h2>

<p>于是踏上了前往南极旅程，降落到恶劣的环境下，极目四望尽是冰川。我们尝试着寻找企鹅的踪迹。跟随Benny的引导，我们径直来到一块空荡荡的冰面上，四周充满了可怕的寂静。在一块大石头的顶端，超特级量子器，蓝宝石Dropbox核心赫然在目！诡异的是，似乎已经有人来过这里，积雪上存有清晰的脚印。我并没有想太多，因为核心就在那里。我将它插入时间机器里，机器似乎重新开始工作了。终于可以回家了！漫长而曲折的旅程终于结束了。再见了，未来！</p>

<p>P.S. 我一直在做<a href="https://www.dropbox.com/home/Dropquest%202012/Captain's%20Logs">船长记录</a>，如果你想看看过去的经历的话。</p>

<p>游戏看上去到这里似乎结束了，但是这根本不像是要通关的样子啊。打开船长记录中的<code>Chapter 12.txt</code>文件：</p>

<blockquote><p>好像出了点问题……但是幸运的是，我太激动了以至于忘了把你带上飞船了！你能帮我把这个文件的跟踪日志设置回我们上次遇见的时候吗？只能指望你了……你是我唯一的希望！</p><footer><strong>Chapter 12.txt</strong></footer></blockquote>


<p>在Dropbox网页版上选择查看这个文件之前的版本，并恢复成老版本。</p>

<h2>第十三关</h2>

<p>重新打开<code>Chapter 12.txt</code>：</p>

<blockquote><p>原来核心被污染了。幸亏还有你这样的好哥们儿。我突然被卷进了一个时空交织的虚无之地。时间机器的通信模块探索不到边界了，没有这个模块，我就只能呆在迷失域里永远回不去了。我感觉Benny其实是在阻止我们回家，但是他为什么要那么做呢？为什么他会把超特级量子器，蓝宝石Dropbox核心放在那么容易得到的地方？我觉得一定有什么地方出了问题，想要走出困境只能靠我们自己了。</p><p>我通过核心的参考手册中一个来自于古老电子游戏中的盾牌重新联系了工程团队。这是我们回到过去的唯一办法。</p><footer><strong>Chapter 12.txt</strong></footer></blockquote>


<p>打开<a href="https://www.dropbox.com/help">Dropbox帮助页面</a>，那里果然有一个盾牌图标。这个盾牌是著名游戏《塞尔达传说》的主角林克<a href="http://www.amazon.com/Full-Hylian-Zelda-Shield-Handle/dp/B001ECV764">所持</a>。</p>

<p><img src="http://img.dayanjia.com/di/IB1D/51h-CtwrVUL.jpg"></p>

<p>点击那个图标。</p>

<h2>第十四关</h2>

<p>听取了Emily的建议，我们来到了艾尔斯巨石附近，或许能找到下一个线索。她说那里的温度让人感觉特级量子器，蓝宝石Dropbox核心似乎曾出现过，我们应该去找找有没有有价值的东西。我们走过一片废墟，看到墙上出现了一些清晰的石刻，和一些可以移动的石块。嗯，我想知道这该如何匹配起来……</p>

<p><img src="http://img.dayanjia.com/di/KV5W/sudoku0.png"></p>

<p>这是一个数独题，如果你对数独很感兴趣当然可以自己解决，不过现成的工具也是很丰富的。我使用的是<a href="http://www.google.com/mobile/goggles/">Google Goggles</a>，只需要把题目拍下来，Google就能解答出来了。解答出来后，拼出题目蓝色框中的部分即可。</p>

<p><img src="http://img.dayanjia.com/di/FCI7/sudoku.png"></p>

<h2>第十五关</h2>

<p>解决了这个谜题，巨石突然开始移动了。眼前出现了……我的时间机器？哦不，有些不同……好像变新了，各个部件也变得更加精致。这时，电脑上弹出了一条信息，是来自Benny的。</p>

<blockquote><p>如果你收到了这条信息，那意味着出了点问题。我来帮你应付，因为时间已经不多了。</p><p>我就是你。</p><p>好吧，是过去的你的未来形象，准确地说。你的时间线有点与众不同（这正是你现在身处窘境的原因），因为那超特级量子器，蓝宝石Dropbox核心事实上把未来的时间带走了，也就是我的时间。其实，我在许多年前制造了时间机器（2038年），但是在研究完成后，一个邪恶的人希望能使用Dropbox核心的能量来改变历史。与其把核心拱手让人，我还不如乘坐时间机器回到你的2006年，把核心交给你保管。没有了核心，我就只能看着你建造你自己的时间机器。我根本没有想到你会愚蠢到来到未来;)。</p><p>没有Dropbox核心，整个世界都变得一团糟。在2050年发生了一场大灾难，我幸存了下来。同时，我发现你会毫无征兆地跳跃到未来（变异的电离层是发生降落事故的原因）。但是你还是可以回家的。使用我自己的时间机器，我已经完成了你需要的关键部件，并设置了固定的程序确保你能每一步都在我的引导之下。我没有足够的原始材料制作另一个Dropbox核心了，但是如果能把你我的部件整合起来，就足够使用了。</p><p>不过，把你引到这里来的原因是，核心被偷走了。邪恶的敌人总是不出不再，他们可能再次将核心用在危险的地方。我有一段脚本能跟踪核心位置，不过我们得快点了。给我（savior@dropbox.com）共享一个新的文件夹，我们赶快动身。</p><footer><strong>Benny</strong></footer></blockquote>


<p>根据指示，在Dropbox新建一个文件夹，并把它共享给对应邮箱。接着共享文件夹里就会出现一些新文件。</p>

<h2>第十六关</h2>

<p>共享文件夹里有一个<code>Notes.txt</code>和7个数字命名的文件。</p>

<blockquote><p>你需要赶紧动身去图书馆，我给了你留了一些文章，但是你会注意到它们会有些不同。你把它当作一个疯狂填词游戏就好。句子里的许多单词并不正确，一旦你找到了这些单词，你必须知道这些单词都没有头。待你想出结果后，下一个目的地是dropbox.com上同时满足两个条件的地方。</p><footer><strong>Notes.txt</strong></footer></blockquote>


<p>Google一下可以很快找到如下结果：</p>

<ul>
<li>第一篇文章来自<a href="http://www.pcmag.com/article2/0,2817,2371504,00.asp">PC Mag杂志对Dropbox iPad版的评测</a>，不同的单词是<code>evokes</code>&lt;=><code>complements</code>。</li>
<li>第二篇文章来自<a href="https://www.dropbox.com/news/20120423">Dropbox的的一则新闻</a>，不同的单词是<code>lives</code>&lt;=><code>life</code>。</li>
<li>第三篇文章中不同的单词是<code>ourselves</code>&lt;=><code>users</code>。</li>
<li>第四篇文章中不同的单词是<code>astronomical</code>&lt;=><code>hulking</code>。</li>
<li>第五篇文章中不同的单词是<code>lasting</code>&lt;=><code>encrypted</code>。</li>
<li>第六篇文章中不同的单词是<code>deal</code>&lt;=><code>rate</code>。</li>
<li>第七篇文章中不同的单词是<code>iridescent</code>&lt;=><code>neon</code>。</li>
</ul>


<p>接下来就要发挥联想给那些单词加上“头”并且使其仍然是一个单词了：</p>

<ul>
<li><strong>R</strong>evokes</li>
<li><strong>O</strong>lives</li>
<li><strong>Y</strong>ourselves</li>
<li><strong>G</strong>astronomical</li>
<li><strong>B</strong>lasting</li>
<li><strong>I</strong>deal</li>
<li><strong>V</strong>iridescent</li>
</ul>


<p>这些“头”连起来便是<a href="http://en.wikipedia.org/wiki/Roy_G._Biv">Roy G. Biv</a>，彩虹的七种颜色。</p>

<p>于是接下来要在dropbox.com上找到同时满足两个条件的地方，也就是同时有两个彩虹的地方。只有<a href="https://www.dropbox.com/share">共享页面</a>上有两个彩虹，点击大的那一个。</p>

<h2>第十七关</h2>

<p>发现了双彩虹的秘密后，我们来到了维多利亚瀑布。感谢大自然，如此壮丽的景观没有被大灾变毁坏，跟我在照片中看到的一样震撼。刚到那里，我们就遇到了那难以捉摸的时光大盗！他们知道我们是来找他的，于是立即乘车逃跑了。他们逃跑的是如此匆忙以至于掉了一部手机！捡起手机，发现那上面似乎有些加密的内容：</p>

<pre><code>DNMOYASUYDEATNSDEEYDAWUHDRYASTRAFYIDTRUSYADA

LUSHBAFDCOODEPYYPGRUMPLEESYNESEZY

STAUURIMEINGANCCREELOGRIVOBRAILSCRPOIOTARGISTUSAIPRICRNOCAARQAUISUSPEICS

CERMRYUEVSUNTHEARMRASPUJTIERTSAURNRUUNAS

NEVYLUGTONYTUSLTPIDERTHOLSARTHW

TRAXOGRIETBRATIBDGRAONKNASETAOGKONMEYOROSTREGDOIPG

LIHEMUENONPYKORTNONXENRDAON

DRERANGEOLOWEYLRGENELBUELIVOTE
</code></pre>

<p>正当我们研究这些内容时，又收到了Benny的新信息。</p>

<blockquote><p>啊，看来那些时光大盗对Dropbox的历史非常熟悉！他们使用了一种Dropbox早期就放弃使用的加密方法。使用这种加密方法，文件被分割成不均匀的块，每块内容都被混淆过。这种加密方法也不是很安全，所以你应该能够解开它！</p><footer><strong>Benny</strong></footer></blockquote>


<p>因为字母的顺序都被搞乱了，而且中间没有空格，所以看起来还是有点头晕的，还原结果如下：</p>

<ul>
<li><p>第一行，是每周日期的名称，缺失<strong>S</strong>UNDAY。</p>

<pre><code>DNMOYA SUYDEAT NSDEEYDAW UHDRYAST RAFYID TRUSYADA
MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY
</code></pre></li>
<li><p>第二行，是《白雪公主和七个小矮人》中七个小矮人的名字，缺失<strong>H</strong>APPY</p>

<pre><code>LUSHBAF DCO ODEPY YPGRUM PLEESY NESEZY
BASHFUL DOC DOPEY GRUMPY SLEEPY SNEEZY
</code></pre></li>
<li><p>第三行，是十二星座，缺失<strong>A</strong>RIES</p>

<pre><code>STAUUR IMEING ANCCRE ELO GRIVO BRAIL SCRPOIO TARGISTUSAI PRICRNOCA ARQAUISU SPEICS
TAURUS GEMINI CANCER LEO VIRGO LIBRA SCORPIO SAGITTARIUS CAPRICORN AQUARIUS PISCES
</code></pre></li>
<li><p>第四行，是八大行星，缺失<strong>N</strong>EPTUNE</p>

<pre><code>CERMRYU EVSUN THEAR MRAS PUJTIER TSAURN RUUNAS
MERCURY VENUS EARTH MARS JUPITER SATURN URANUS
</code></pre></li>
<li><p>第五行，是七宗罪，缺失<strong>G</strong>REED</p>

<pre><code>NEVY LUGTONYT USLT PIDER THOLS ARTHW
ENVY GLUTTONY LUST PRIDE SLOTH WRATH
</code></pre></li>
<li><p>第六行，是十二生肖，缺失<strong>H</strong>ORSE</p>

<pre><code>TRA XO GRIET BRATIB DGRAON KNASE TAOG KONMEY OROSTRE GDO IPG
RAT OX TIGER RABBIT DRAGON SNAKE GOAT MONKEY ROOSTER DOG PIG
</code></pre></li>
<li><p>第七行，是惰性气体，缺失<strong>A</strong>RGON</p>

<pre><code>LIHEMU ENON PYKORTN ONXEN RDAON
HELIUM NEON KRYPTON XENON RADON
</code></pre></li>
<li><p>第八行，是彩虹颜色，缺失<strong>I</strong>NDIGO</p>

<pre><code>DRE RANGEO LOWEYL RGENE LBUE LIVOTE
RED ORANGE YELLOW GREEN BLUG VIOLET
</code></pre></li>
</ul>


<p>所有缺失的单词的首字母连起来得到答案<em>SHANGHAI</em>。</p>

<h2>第十八关</h2>

<p>我们需要到他们在上海的基地去一趟。基地隐藏在一家购物中心的地下室中，空间十分狭小。电路图铺满了地面，跟我时间机器内的结构非常类似。墙上也钉满了各种零件。此外，桌上还放着一个不知名的巨大物体。为什么他们会把这么一大块垃圾放在这里呢？我觉得他们即使是我的敌人，这么脏乱差的房间我也看不下去，帮他们打扫一下吧。我开始把<a href="https://www.dropbox.com/home/Dropquest%202012/Spring%20Cleaning">物品</a>一一归类。我需要到Dropbox的网站里操作，这样会方便一些。</p>

<p>Dropquest 2012下多了一个 <code>Spring Cleaning</code>文件夹，里面有10张图片，此外还有<code>Category 1</code>和<code>Category 2</code>两个文件夹，里面分别是一张水滴和箱子的图片，暗指了两个分类。在Dropbox网页版上将10张图片移动到这两个分类文件夹里即可过关。</p>

<p>第一个带有水滴的分类暗指“Drop”，10张图片中能和Drop组成词组的有：</p>

<ul>
<li>1 Drop ship（直接发货）</li>
<li>3 Drop shadow（投影）</li>
<li>6 Drop dead（去死吧）</li>
<li>8 Drop off（下降）</li>
<li>9 Drop shot（扣球）</li>
</ul>


<p>而第二个箱子的分类则暗指“Box”，剩下的图片都能和Box搭配：</p>

<ul>
<li>2 Fuse box（保险丝盒）</li>
<li>4 Ballot box（投票箱）</li>
<li>5 Match box（火柴盒）</li>
<li>7 Juice box（果汁盒）</li>
<li>10 Cereal box（谷类食品盒）</li>
</ul>


<h2>第十九关</h2>

<p>大清理效果不错！当所有物品都被归类后，基地的电力突然激活了。但是没有出现什么计算机，却出现了一道光。一些聚光灯照亮了一堵特别的墙……感觉就像一块画满了照片、图表和时间机器电路图的公告板。图案中间是这样的：</p>

<p><img src="http://img.dayanjia.com/di/RPQV/shmodel2.png"></p>

<p>显然这个谜题是想让我们想出1、2、3代表的内容，然后构成<code>1.2/3</code>的格式。</p>

<ol>
<li>一个汉字和朝鲜文，他们分别发D和B的音</li>
<li>一辆汽车，<a href="http://en.wikipedia.org/wiki/Audi_TT">奥迪TT</a></li>
<li>一组扑克牌谜题。将J、Q、K、A分别看作11、12、13、14后，最后一行的牌应当是相应列前两张大数减去小数的结果。因此框出的最后一行应该为Q2J9J4。不同人可能会看到不同的扑克牌，但是计算方法是一样的。</li>
</ol>


<p>接着，访问<code>db.tt/Q2J9J4</code>，将出现的<code>Rejected Dropbox Movie Ideas</code>添加到自己的Dropbox中。</p>

<h2>第二十关</h2>

<p>添加进来的7个文本文件中，有一个说明文件，还有6个数字编号的文件。</p>

<blockquote><p>回到我最初的时间中，他们给Dropbox拍了一些特色电影。这些电影中，有一些在最终制作中被否决的想法。我整理了一些档案供你查看。</p><p>你需要从这些文档中找到密码，找到后打开`dropbox.com/dropquest2012/[password]`（不带空格）。</p><footer><strong>Notes.txt</strong></footer></blockquote>


<p>当然，不存在什么关于Dropbox的电影，那六份文档中的剧情都是模仿一些有名电影的。</p>

<blockquote><p>刚从MIT毕业，Drew Houston就发现他的高中同学从父亲那里继承了拉斯维加斯的三家赌场。于是他伙同自己的老朋友Arash Ferdowsi和一帮精英软件工程师，假借Dropbox为名，入侵了赌场系统，盗取金钱。</p><footer><strong>1.txt</strong></footer></blockquote>


<p>此剧情山寨自《十一罗汉》（Ocean&#8217;s Eleven）。</p>

<blockquote><p>Drew和Arash招募了世界上一群富有非凡才能的软件工程师加入了Dropbox。在那里，他们互相帮助对方驾驭自己的软件超能力。Drew尽力使Arash和其他人相信他们可以和普通人和平共处，但是Arash觉得他们就是不同于一般人。最终，Arash带着支持自己的一部分人和Dropbox的其他成员分道扬镳。</p><footer><strong>2.txt</strong></footer></blockquote>


<p>没错，这是《X战警：第一战》（X-Men: First Class）的剧情。</p>

<blockquote><p>Drew和Arash在开发Dropbox的早期，发现资金不够了。他们没有潜在的投资者，于是他们决定去拉斯维加斯靠玩21点赚钱。</p><footer><strong>3.txt</strong></footer></blockquote>


<p>这是一部坑爹的名为《决胜21点》（21）的电影。</p>

<blockquote><p>早在孩童时期，Drew和Arash目击了一场发生在俄亥俄乡间的火车脱轨事故。经过一番调查，他们发现这辆火车装着一个在地球上紧急迫降的外星人。他们利用外星人的先进技术发明了Dropbox文件同步系统。</p><footer><strong>4.txt</strong></footer></blockquote>


<p>后面的内容真是越来越扯，不过根据火车和外星人还是能猜出这是《超级8》（Super 8）。</p>

<blockquote><p>英国的一次病毒爆发引起了暴乱。Drew和Arash创造了Dropbox，因此被感染的人群依然能够恢复自己的文件。唯一的问题是，被感染的人会在安装Dropbox客户端之前就狂暴地摧毁自己的电脑。</p><footer><strong>5.txt</strong></footer></blockquote>


<p>狂怒地杀人成了砸电脑，剧情取自《惊变28天》（28 Days Later&#8230;）。</p>

<blockquote><p>在电影的开始，Arash因为自己的心理问题而找到了Drew。Arash会幻觉看到死去的软件公司。Drew认识到，想要治愈Arash，就得从过去的公司中吸取教训，让Dropbox变成一家成功的企业。</p><footer><strong>6.txt</strong></footer></blockquote>


<p>这灵异的故事源自于《第六感》（The Sixth Sense）。</p>

<p>当我们找到这六部电影后，会发现他们的片名中都带有数字。在毫无头绪之际，让我们再次审视着六段文本。</p>

<ul>
<li>第一篇的第11个单词：Houston</li>
<li>第二篇的第1个单词：We</li>
<li>第三篇的第21个单词：have</li>
<li>第四篇的第8个单词：a</li>
<li>第五篇的第28个单词：problem</li>
<li>第六篇的第6个单词：movie</li>
</ul>


<p>前五个单词连成一句话：Houston, we have a problem。这正是一部著名电影《阿波罗13号》中的经典台词。所以答案是<em>apollo13</em>。</p>

<h2>第二十一关</h2>

<p>大声说出了密码，一块秘密的面板滑出，随之一台电脑出现在眼前，屏幕上显示着不可思议的文字……嗯……也许这可以知道那些家伙到底是干嘛的。</p>

<pre><code>THE QUICK WN F JUMS VE THE LAZY G

THA QOECK BRIWN FIX JOMPS IVAR THA LUZY DIG

THA QECK IWN FI JMS IVA THA LUZY IG

QUICK JUMS LAZY

QUICK BROWN JUMPS OVER LAZY

THU QIACK BREWN FEX JIMPS EVUR THU LOZY DEG

EHT KCIUQ BROWN FOX JUMPS REVO EHT LAZY DOG

THA QOECK WN F JOMS VA THA LUZY G

AHT KCEOQ BRIWN FIX JOMPS IVAR AHT LUZY DIG
</code></pre>

<p>正当我们阅读屏幕上的文字时，Benny又来信息了。</p>

<blockquote><p>看上去这是用了Dropbox放弃使用的另一种加密方法。这个比之前的要复杂些，所以仔细听好了。</p><p>Drew和Arash觉得把文件分块并没有什么用，于是他们放弃了那个想法，寻找更加复杂的加密方法。他们头脑风暴了许多想法，最终他们认为组合使用多个加密函数会让数据更加安全。他们通过头脑风暴，决定使用四个函数：</p><p>函数1：移除在DROPBOX中出现的字母<br/>函数2：将每个元音用它前一个来替代（a→u, e→a, i→e, o→i, u→o）<br/>函数5：对于每个由字母表后半部分字母开头的单词，反转它的顺序<br/>函数9：移除所有只有3个字母或更少的单词</p><p>看上去时光大盗将同一句话加密了数次，使用了一个或两种上面的函数（或者是同一个函数两次）。如果你能得出他们使用了哪些函数，你应该就能解锁他们的电脑。</p><footer><strong>Benny</strong></footer></blockquote>


<p>仔细观察那些密文，可以发现都来自一句著名句子“The quick brown fox jumps over the lazy dog.”，这句话是一个比较简短但是使用了全部26个字母的句子，经常出现在计算机文字排印中。我们先来实现这些函数，省得之后手动进行加密的工作。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="k">def</span> <span class="nf">f1</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
</span><span class='line'>    <span class="k">return</span> <span class="nb">filter</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span> <span class="ow">not</span> <span class="ow">in</span> <span class="s">&quot;DROPBOX&quot;</span><span class="p">,</span> <span class="n">s</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'><span class="k">def</span> <span class="nf">f2</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
</span><span class='line'>    <span class="n">shift</span> <span class="o">=</span> <span class="p">{</span><span class="s">&#39;A&#39;</span><span class="p">:</span> <span class="s">&#39;U&#39;</span><span class="p">,</span> <span class="s">&#39;E&#39;</span><span class="p">:</span> <span class="s">&#39;A&#39;</span><span class="p">,</span> <span class="s">&#39;I&#39;</span><span class="p">:</span> <span class="s">&#39;E&#39;</span><span class="p">,</span> <span class="s">&#39;O&#39;</span><span class="p">:</span> <span class="s">&#39;I&#39;</span><span class="p">,</span> <span class="s">&#39;U&#39;</span><span class="p">:</span> <span class="s">&#39;O&#39;</span><span class="p">}</span>
</span><span class='line'>    <span class="k">return</span> <span class="s">&#39;&#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">shift</span><span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="k">if</span> <span class="n">x</span> <span class="ow">in</span> <span class="s">&quot;AEIOU&quot;</span> <span class="k">else</span> <span class="n">x</span><span class="p">,</span> <span class="n">s</span><span class="p">))</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'><span class="k">def</span> <span class="nf">f5</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
</span><span class='line'>    <span class="k">return</span> <span class="s">&#39; &#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="k">if</span> <span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&gt;=</span> <span class="s">&#39;N&#39;</span> <span class="k">else</span> <span class="n">x</span><span class="p">,</span> <span class="n">s</span><span class="o">.</span><span class="n">split</span><span class="p">()))</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'><span class="k">def</span> <span class="nf">f9</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
</span><span class='line'>    <span class="k">return</span> <span class="s">&#39; &#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">filter</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="nb">len</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">3</span><span class="p">,</span> <span class="n">s</span><span class="o">.</span><span class="n">split</span><span class="p">()))</span>
</span></code></pre></td></tr></table></div></figure>


<p>接着我们便可以将这四个函数单独使用和两两组合，看看得到的密文都是什么样的。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="n">sentence</span> <span class="o">=</span> <span class="s">&quot;THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG&quot;</span>
</span><span class='line'><span class="n">func</span> <span class="o">=</span> <span class="p">[</span><span class="n">f1</span><span class="p">,</span> <span class="n">f2</span><span class="p">,</span> <span class="n">f5</span><span class="p">,</span> <span class="n">f9</span><span class="p">]</span>
</span><span class='line'>
</span><span class='line'><span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">func</span><span class="p">:</span>
</span><span class='line'>    <span class="k">print</span> <span class="n">f</span><span class="o">.</span><span class="n">__name__</span>
</span><span class='line'>    <span class="k">print</span> <span class="n">f</span><span class="p">(</span><span class="n">sentence</span><span class="p">)</span>
</span><span class='line'>    <span class="k">print</span> <span class="s">&#39;-&#39;</span> <span class="o">*</span> <span class="mi">40</span>
</span><span class='line'>
</span><span class='line'><span class="k">for</span> <span class="n">f1</span> <span class="ow">in</span> <span class="n">func</span><span class="p">:</span>
</span><span class='line'>    <span class="k">for</span> <span class="n">f2</span> <span class="ow">in</span> <span class="n">func</span><span class="p">:</span>
</span><span class='line'>        <span class="k">print</span> <span class="s">&quot;</span><span class="si">%s</span><span class="s"> then </span><span class="si">%s</span><span class="s">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">f1</span><span class="o">.</span><span class="n">__name__</span><span class="p">,</span> <span class="n">f2</span><span class="o">.</span><span class="n">__name__</span><span class="p">)</span>
</span><span class='line'>        <span class="k">print</span> <span class="n">f2</span><span class="p">(</span><span class="n">f1</span><span class="p">(</span><span class="n">sentence</span><span class="p">))</span>
</span><span class='line'>        <span class="k">print</span> <span class="s">&#39;-&#39;</span> <span class="o">*</span> <span class="mi">40</span>
</span></code></pre></td></tr></table></div></figure>


<pre><code>f1
THE QUICK WN F JUMS VE THE LAZY G
----------------------------------------
f2
THA QOECK BRIWN FIX JOMPS IVAR THA LUZY DIG
----------------------------------------
f5
EHT KCIUQ BROWN FOX JUMPS REVO EHT LAZY DOG
----------------------------------------
f9
QUICK BROWN JUMPS OVER LAZY
----------------------------------------
f1 then f1
THE QUICK WN F JUMS VE THE LAZY G
----------------------------------------
f1 then f2
THA QOECK WN F JOMS VA THA LUZY G
----------------------------------------
f1 then f5
EHT KCIUQ NW F JUMS EV EHT LAZY G
----------------------------------------
f1 then f9
QUICK JUMS LAZY
----------------------------------------
f2 then f1
THA QECK IWN FI JMS IVA THA LUZY IG
----------------------------------------
f2 then f2
THU QIACK BREWN FEX JIMPS EVUR THU LOZY DEG
----------------------------------------
f2 then f5
AHT KCEOQ BRIWN FIX JOMPS IVAR AHT LUZY DIG
----------------------------------------
f2 then f9
QOECK BRIWN JOMPS IVAR LUZY
----------------------------------------
f5 then f1
EHT KCIUQ WN F JUMS EV EHT LAZY G
----------------------------------------
f5 then f2
AHT KCEOQ BRIWN FIX JOMPS RAVI AHT LUZY DIG
----------------------------------------
f5 then f5
EHT KCIUQ BROWN FOX JUMPS OVER EHT LAZY DOG
----------------------------------------
f5 then f9
KCIUQ BROWN JUMPS REVO LAZY
----------------------------------------
f9 then f1
QUICK WN JUMS VE LAZY
----------------------------------------
f9 then f2
QOECK BRIWN JOMPS IVAR LUZY
----------------------------------------
f9 then f5
KCIUQ BROWN JUMPS REVO LAZY
----------------------------------------
f9 then f9
QUICK BROWN JUMPS OVER LAZY
----------------------------------------
</code></pre>

<p>和前面的密文对比，可以发现他们分别使用的是：</p>

<ul>
<li>f1</li>
<li>f2</li>
<li>f2 then f1</li>
<li>f1 then f9</li>
<li>f9</li>
<li>f2 then f2</li>
<li>f5</li>
<li>f1 then f2</li>
<li>f2 then f5</li>
</ul>


<p>去字母表中找到第1, 2, 21, 19, 9, 22, 5, 12, 25个字母，组成答案<em>ABUSIVELY</em>。</p>

<h2>第二十二关</h2>

<p>于是我们终于得到了进入电脑的密码，或许我们可以定位他们真正的基地位置了。突然间，屏幕上显示出一张大地图，看上去像是一些数学问题，但是那些数字都是……国旗吗？屏幕上还有一些网站列表，一张元素周期表，还有<a href="https://www.dropbox.com/home/Dropquest%202012/Time%20Bandits'%20Greatest%20Hits">一些音乐</a>……？这到底是要闹哪样？</p>

<p><img src="http://img.dayanjia.com/di/MTSS/Dropquest2012Chapter22.png"></p>

<p>解密进行到这里越来越有挑战性了。这一关的线索比较多，很容易迷失方向。注意本关不同人显示的表达式可能不一样。</p>

<p>先从旗帜下手，首先你需要认得这些旗帜对应的国家（地区），然后想办法将其转换成数字，以算出每个表达式的值。</p>

<p>ISO 3166-1定义了大部分国家（地区）的两位字母代码，可以在<a href="http://en.wikipedia.org/wiki/ISO_3166-1">维基百科页面</a>上查到图中出现的各个旗帜对应的字母代码。</p>

<ul>
<li>[(古巴CU * 纳米比亚NA) + 马达加斯加MG] mod 163</li>
<li>[(密克罗尼西亚联邦FM / 塞内加尔SN) * 格鲁吉亚GE] mod 23</li>
<li>[葡萄牙PT - 俄罗斯RU - 列支敦士登LI] mod 18</li>
<li>[(阿根廷AR * 塞舌尔SC) / 澳门MO] mod 5</li>
<li>[(波黑BA + 印度IN) / 巴西BR] mod 2</li>
</ul>


<p>注意到<a href="http://zh.wikipedia.org/wiki/%E5%85%83%E7%B4%A0%E5%91%A8%E6%9C%9F%E8%A1%A8">元素周期表</a>，上面中的两位字母代码都能在元素周期表中找到相应的元素，于是表达式被转换。</p>

<ul>
<li>[(铜Cu * 钠Na) + 镁Mg] mod 163</li>
<li>[(镄Fm / 锡Sn) * 锗Ge] mod 23</li>
<li>[铂Pt - 钌Ru - 锂Li] mod 18</li>
<li>[(氩Ar * 钪Sc) / 钼Mo] mod 5</li>
<li>[(钡Ba + 铟In) / 溴Br] mod 2</li>
</ul>


<p>最后用相应元素的原子量（即编号）带入表达式中，便可以算出它们的值了。</p>

<ul>
<li>[(29 * 11) + 12] mod 163 = 5</li>
<li>[(100 / 50) * 32] mod 23 = 18</li>
<li>[78 - 44 - 3] mod 18 = 13</li>
<li>[(18 * 21) / 42] mod 5 = 4</li>
<li>[(56 + 49) / 35] mod 2 = 1</li>
</ul>


<p>这时候就该轮到那些音乐文件了，每个MIDI音乐的内容都是几个钢琴和鼓的音。在Dropquest 2012下的<code>Time Bandits' Greatest Hits</code>文件夹下分别打开5, 18, 13, 4, 1这五个mid文件，仔细听其中的音，分别用C, D, E, F, G, A, B来表示音符。</p>

<ul>
<li>5.mid: (鼓) E G</li>
<li>18.mid: D E (鼓) F</li>
<li>13.mid: (鼓) E A F</li>
<li>4.mid: E G (鼓)</li>
<li>1.mid: C E D (鼓)</li>
</ul>


<p>每段音乐都有一个鼓声，代表一个空缺的音符（字母）。寻找一个A-G范围内的字母填入空缺处使其能组合成一个单词，将这些空缺的字母连起来，便是答案。本例中的答案便是<em>BADGE</em>。</p>

<p>完整起见，这里给出不同的表达式文件名对应最终的mid文件和空缺部分列出来：</p>

<ul>
<li>flags0.png - 11.mid - (F) A C E</li>
<li>flags1.png - 18.mid - D E (A) F</li>
<li>flags2.png - 01.mid - C E D (E)</li>
<li>flags3.png - 14.mid - (C) A B B A G E</li>
<li>flags4.png - 19.mid - D E C (A) D E</li>
<li>flags5.png - 05.mid - (B) E G</li>
<li>flags6.png - 04.mid - E G (G)</li>
<li>flags7.png - 13.mid - (D) E A F</li>
</ul>


<h2>第二十三关</h2>

<p>我有一种强烈的预感，快要接近事实真相了。在提交了密码后，我看到了一个充满了各种诡异符号的数据库。我唯一能看出的只有这个：<a href="http://www.twitter.com/dropquestalpha">http://www.twitter.com/dropquestalpha</a>。</p>

<p>打开这个Twitter用户的页面，可以看到他在自己的个人资料地区项中写下了“18.836043, -72.545993”这个坐标。同时可以看到他关注了Dropquest Beta、Dropquest Mu、Dropquest Kappa、Dropquest Delta这几个用户。顺藤摸瓜，可以将所有希腊字母的用户资料中的坐标获取到。</p>

<ul>
<li>@DropquestAlpha: 18.836043, -72.545993</li>
<li>@DropquestBeta: 30.836043, -60.545993</li>
<li>@DropquestGamma: 14.836043, -36.545993</li>
<li>@DropquestDelta 2.836043, -48.545993</li>
<li>@DropquestEpsi: -9.163957, -36.545993</li>
<li>@DropquestZeta: -25.163957, -60.545993</li>
<li>@DropquestEta: ???</li>
<li>@DropquestTheta: -25.163957, -84.545993</li>
<li>@DropquestIota: -9.163957, -108.545993</li>
<li>@DropquestKappa: 2.836043, -96.545993</li>
<li>@DropquestLambda: 14.836043, -108.545993</li>
<li>@DropquestMu: 30.836043, -84.545993</li>
<li>@DropquestNu: -25.163957, -96.545993</li>
<li>@DropquestXi: -41.163957, -72.545993</li>
<li>@DropquestOmi: -25.163957, -48.545993</li>
<li>@DropquestPi: 49.156113, 9.714661</li>
<li>@DropquestRho: 52.359405, 5.221231</li>
<li>@DropquestSigma: 51.778936, -1.268085</li>
<li>@DropquestTau: 账户被停用</li>
<li>@DropquestUpsilo: 14.836043, -36.545993（和@DropquestGamma相同）</li>
<li>@DropquestPhi: 不存在</li>
<li>@DropquestChi: 不存在</li>
<li>@DropquestPsi: The Bootylicious Brothel</li>
<li>@DropquestOmega: 名叫“Hans Wurst”，无位置信息</li>
</ul>


<p>去Google Map中将这些坐标标出来。</p>

<iframe width="425" height="350" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="https://maps.google.com/maps/ms?msa=0&amp;msid=211054947841122726328.0004c014d2983c70600b8&amp;hl=zh_CN&amp;ie=UTF8&amp;t=v&amp;ll=5.597724,-49.415666&amp;spn=93.523362,118.260654&amp;output=embed"></iframe>


<br /><small>在较大的地图中查看<a href="https://maps.google.com/maps/ms?msa=0&amp;msid=211054947841122726328.0004c014d2983c70600b8&amp;hl=zh_CN&amp;ie=UTF8&amp;t=v&amp;ll=5.597724,-49.415666&amp;spn=93.523362,118.260654&amp;source=embed" style="color:#0000FF;text-align:left">Dropquest 2012 Chapter 23</a></small>


<p>可以发现大多数集中在南美地区，而且这些点连起来酷似一个Dropbox的图标。如果把Dropbox的图标叠上去的话会看得更清楚，而且会发现还少了一个点。在地图上标出这个点后，放大一看，原来在秘鲁的马丘比丘。</p>

<p><img src="http://img.dayanjia.com/di/SVHE/dropbox-map.png"></p>

<p>所以答案便是马丘比丘，<em>MACHUPICCHU</em>。</p>

<h2>终章</h2>

<p>我们最终来到了基地，在马丘比丘的中心地带。这里的废墟依然跟我在照片中看到的一样，但是在中央区域却多出了一座大型建筑。周围没有多少人——最多10个人。我很惊讶，因为这是我第一次有机会看到未来人的日常生活是怎样的……不过看上去依然很普通。我猜这个基地可能是个大型工厂什么的。应该到处是大型机械，这里的人都非常熟悉使用这些机械。幸运的是，他们正在吃晚饭！我闻到了通心粉和奶酪的味道，但是我更感兴趣的是我发现了……他们制造的时间机器没有人看管！略微施展一下忍者轻功，我从一块阴影跳到另一块阴影，于是我和这些机器近在咫尺了。我认识这些电路图……它们一定使用我或者Benny处偷来的。我小心翼翼地准备把超特级量子器，蓝宝石Dropbox核心从机器上拆下来。突然间，警报响了！</p>

<p>我立刻跳进我的时间机器里，不能让他们抓到。这时候，我的通讯器响了，一条消息传来：</p>

<blockquote><p>根据时间电流，你只能从固定地方回到过去——所谓的时间之门。你需要想清楚你要去的地方是哪里！</p></blockquote>


<p>通讯突然中断了，但是一张图出现在屏幕上。我们正要去穿越世界，但是到底该去哪里呢？我们如何在逃跑的时候搞定这一切？</p>

<p><img src="http://img.dayanjia.com/di/983Q/crossword.png"></p>

<p>这是一张巨大的填字游戏，其中第20和61号的横行将给出两条提示。做完填字结果是这样的：</p>

<p><img src="http://img.dayanjia.com/di/NEYV/crossword.png"></p>

<p>两条提示分别是<code>ORDERLOGSBYSIZE</code>和<code>NINELETTERWORDS</code>，即“按大小排列”和“9个字母的单词”。那究竟是把什么按大小排列呢？</p>

<p>你可以想到了，便是每关都会留下的船长日志。将这些文本文件按大小升序排列，然后找到每个文件中的9个字母的单词。</p>

<ul>
<li>Chapter 6.txt   - nominally</li>
<li>Chapter 10.txt  - timepiece</li>
<li>Chapter 8.txt   - histories</li>
<li>Chapter 7.txt   - weirdness</li>
<li>Chapter 1.txt   - obnoxious</li>
<li>Chapter 13.txt  - rebounded</li>
<li>Chapter 2.txt   - dominated</li>
<li>Chapter 18.txt  - seemingly</li>
<li>Chapter 9.txt   - objective</li>
<li>Chapter 5.txt   - fastening</li>
<li>Chapter 23.txt  - Literally</li>
<li>Chapter 21.txt  - operation</li>
<li>Chapter 11.txt  - generated</li>
<li>Chapter 3.txt   - somewhere</li>
<li>Chapter 20.txt  - beguiling</li>
<li>Chapter 4.txt   - yesterday</li>
<li>Chapter 19.txt  - continent</li>
<li>Chapter 14.txt  - headaches</li>
<li>Chapter 15.txt  - acquiring</li>
<li>Chapter 17.txt  - pertained</li>
<li>Chapter 16.txt  - tediously</li>
<li>Chapter 22.txt  - educative</li>
<li>Chapter 12.txt  - reference</li>
</ul>


<p>将首字母连起来，看上去是“NTH WORDS OF LOGS BY CHAPTER”。离最后的成功只有一步之遥了。我们需要将第N章日志中的第N个字母提取出来，是这样一段文字：</p>

<pre><code> The time gate is the Only the of The new seven wonders Of the world in the only continent you haven't visited yet
</code></pre>

<p>时间之门是世界新七大奇迹之一，它在你还没有访问过的一个大洲内。世界新七大奇迹分别是：</p>

<ul>
<li>长城（中国，亚洲）</li>
<li>佩特拉古城（约旦，亚洲）</li>
<li>里约热内卢基督像（巴西，南美洲）</li>
<li>马丘比丘（秘鲁，南美洲）</li>
<li>奇琴伊察玛雅城邦遗址（墨西哥，北美洲）</li>
<li>罗马斗兽场（意大利，欧洲）</li>
<li>泰姬玛哈陵（印度，亚洲）</li>
</ul>


<p>看来我们的终点便是罗马斗兽场（<em>Colosseum</em>）了。</p>

<h2>结语</h2>

<p>罗马，一片废墟。让人感到奇怪的是，许多摩天大楼倒塌了，而远古遗迹却依然完整。我们以最快的速度降落在罗马斗兽场的中心。在广阔的竞技场上，我正为时间跳跃做着最后的准备。时光大盗就在眼前——我们只有几秒钟的时间来完成这一切。倒计时开始了。“五……”时光大盗就在我们几步开外的地方，马上就会跳上我的机器。“四……三……”他们开始拼命地奔向我，挥舞着武器。“二……”他们开始瞄准。“一……”</p>

<p>刹那间，一道爆炸的亮光刺破在他们跟前，瞬间将他们打翻在地。机器突然向后颠簸了一下，准备将我们重新带回了属于自己的时光。我的眼前一片模糊，微笑着。现实飞快地在我们身边扭曲，机器把我带回了我的工作室，一切就像我刚离开时的那样。我们呆坐了好几分钟，好不容易才回过神来。</p>

<p>那个神秘的人究竟是谁？真是Benny吗？不管怎样，我总算是知道了时间是多么地脆弱，Dropbox核心也不应当被滥用。这次时间旅行教会了我许多东西……我现在有了比见证历史更重要的事情，那就是去阻止多年后将会发生的大灾难。我用防水布将时间机器盖住，缓缓地离开了。真是荒诞的一天啊！</p>

<p><img src="http://img.dayanjia.com/di/9HRQ/delorean.png"></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[有趣的递归画图板]]></title>
    <link href="http://blog.dayanjia.com/2012/05/recursive-drawing/"/>
    <updated>2012-05-10T18:25:00+08:00</updated>
    <id>http://blog.dayanjia.com/2012/05/recursive-drawing</id>
    <content type="html"><![CDATA[<p><a href="http://img.dayanjia.com/di/DQT2/recursive_tree.jpg"><img class="right" src="http://img.dayanjia.com/dt/DQT2/recursive_tree.jpg"></a></p>

<p>递归（Recursion）是编程里很有意思的一样东西，简单地讲就是自我调用、自我复制。主流的编程语言都支持函数的递归，在很多函数式语言中递归扮演着十分重要的作用。不过我今天不是来说这些枯燥的东西的，而是介绍一个很有意思的<a href="http://recursivedrawing.com/">递归画图板</a>应用。题图便是用它画出来的一棵“二叉树”。</p>

<!--more-->


<h2><a href="http://recursivedrawing.com/">Recursive Drawing</a></h2>

<p><embed src="http://player.youku.com/player.php/sid/XMzk0MTgyMzY0/v.swf" quality="high" width="480" height="400" align="middle" allowScriptAccess="sameDomain" allowFullscreen="true" type="application/x-shockwave-flash"></embed></p>

<p>(<a href="http://v.youku.com/v_show/id_XMzk0MTgyMzY0.html">HTML5播放</a>)</p>

<p>这个画图应用完全由JavaScript<a href="https://github.com/electronicwhisper/recursive-drawing%20on%20Github">写成</a>，通过简单的圆形和矩形可以制造出千变万化的奇妙图案。作者在视频中演示了二叉树和Fibonacci树的制造过程，图形化的演示非常直观。</p>

<h2>与 <a href="http://www.contextfreeart.org/">Context Free</a></h2>

<p>Recursive Drawing 可以认为是 Context Free 的图形界面版。Context Free是一个通过代码生成递归图形的软件，而Recursive Drawing则是完全的所见即所得。</p>

<h2>分形</h2>

<p>使用Recursive Drawing我们可以创造一些好看的<a href="http://zh.wikipedia.org/zh-cn/%E5%88%86%E5%BD%A2">分形</a>图案。例如我可以先画一个简单的水滴图案：</p>

<p><img src="http://img.dayanjia.com/di/SSCV/recursive_1.gif"></p>

<p>然后把它拼成一个触手：</p>

<p><img src="http://img.dayanjia.com/di/IF2D/recursive_2.gif"></p>

<p>最后可以拼成无数个张牙舞爪的触手：</p>

<p><img src="http://img.dayanjia.com/di/3522/recursive_3.gif"></p>

<p>大家可以发挥自己的想象力，创造出更多有趣的图案。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Ubuntu 12.04 LTS Release Party @Nanjing]]></title>
    <link href="http://blog.dayanjia.com/2012/05/ubuntu-12-dot-04-lts-release-party-at-nanjing/"/>
    <updated>2012-05-06T18:57:00+08:00</updated>
    <id>http://blog.dayanjia.com/2012/05/ubuntu-12-dot-04-lts-release-party-at-nanjing</id>
    <content type="html"><![CDATA[<p>今天去南京信息工程大学参加了Ubuntu 12.04 LTS Release Party。其实这个活动是前一天才偶然看到的，加上可以顺便去那边探望 <a href="https://twitter.com/#!/Werry_Wong">@Werry_Wong</a>，于是便踏上了前往江北的漫漫之路。会场上有好几位同学和老师的Presentation。这次行程最大的的收获是知道了南京不光有仙林开出的D1路公交车，还有D2、D3、D4等好几条线路……呵呵不开玩笑了。</p>

<!--more-->


<h2>『发布会』</h2>

<p><a href="http://img.dayanjia.com/di/2MZG/7ab870e4975711e19dc71231380fe523_7.jpg"><img class="right" src="http://img.dayanjia.com/dt/2MZG/7ab870e4975711e19dc71231380fe523_7.jpg" title="盗张@lty1993的图" ></a></p>

<p>活动的主持人是个来自东大的MM，话说这圈子里有女生的存在真是一件比较奇葩的事情。而且当她称本次活动叫“发布会”的时候我瞬间被惊到了。经过了几年乔布斯的渲染，我已然觉得只有那样的活动才能叫“发布会”，这在一个穷乡僻壤的大学的教室里的活动也能叫发布会么？</p>

<p>不过发布会上还是有介绍Ubuntu 12.04的一些新特性的，包括那个让我觉得很逆天的“HUD”特性。HUD(Heads-Up Display)就是把菜单全部文字可搜索化了，访问菜单项需要进行键盘输入，HUD会根据输入展示匹配的菜单项。这个特性倒是很有Vim里输入<code>:</code>后敲命令的感觉，不过既然Ubuntu是面向桌面用户的，如此专家级的功能会让初学者非常不适应的。虽说显得很激进，但是Unity在新型的人机交互方式上还是做了不少探索，光是这点就值得肯定。</p>

<h2>分享</h2>

<p>我们学校计算机系的夏耐老师现场介绍了他的<a href="http://kerneldedup.org/">UKSM项目</a>。之前在小百合上看到这个项目的时候就有一种“很好很强大”的感觉，今天听到了现场介绍，一股“虽然不知道楼主在说什么，但是感觉很厉害的样子”的情怀涌上心头。简而言之，UKSM是从KVM KSM(Kernel SamePage Merging)衍生而来的一个内核扩展，能够在桌面和服务器操作系统上自动发现相同内容的页表并将其合并，以减少冗余，增加内存使用效率。虽然我对操作系统仅仅一知半解，但是在听的过程中竟然突然想到一个问题。现代操作系统基本都支持<a href="http://en.wikipedia.org/wiki/Address_space_layout_randomization">地址空间布局随机化</a>（ASLR）技术来确保安全性，防止缓冲区溢出攻击什么的，当应用程序采用ASLR的时候会不会对UKSM有影响呢？不过夏老师最后也没有Q&amp;A环节，结束后也没有机会问到。</p>

<p>其他的Presentation就相对比较水了。活动的组织者，一个大一学生，讲了讲Unix、GNU的历史故事，挺绘声绘色的，看得出做了许多准备。另一个东南大学的同学介绍了自己开发的山寨Github项目。还有一个讲了“开源与社交网络”的，没怎么注意听。</p>

<h2>纪念品</h2>

<p>参加这种活动，肯定是要有纪念品的嘛。主办方居然连最常见的安装光盘都没有准备多少（不过现在安装光盘也不常用就是了，无所谓），最后就拿了点笔和徽章了。</p>

<p><img src="http://img.dayanjia.com/di/DUFQ/20c81268977211e180d51231380fcd7e_7.jpg" title="Souvenir" ></p>

<p>最后感谢活动主办者的辛苦劳动，没有志愿者就没有FOSS的今天。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Introducing NanjingAir]]></title>
    <link href="http://blog.dayanjia.com/2012/05/introducing-nanjingair/"/>
    <updated>2012-05-04T14:15:00+08:00</updated>
    <id>http://blog.dayanjia.com/2012/05/introducing-nanjingair</id>
    <content type="html"><![CDATA[<p>2012年3月30日，江苏省开始<a href="http://218.94.78.75/airwebsite/">公布省内的17个监测点的PM2.5数据</a>，同时SO<sub>2</sub>、NO<sub>2</sub>、PM10等实时数据也得以公布。从去年开始炒得沸沸扬扬的“PM 2.5”也终于有了一个半透明的官方公开渠道。至于我为什么说它是半透明，一是因为只有原始数据，没有以此换算而来的空气质量指数（AQI）数据；二是民众对这些数据的获取是被动的，说白了就是一副“数据放在那儿你爱看不看”的姿态。</p>

<p>于是便有了<a href="https://github.com/clippit/NanjingAir">NanjingAir</a>这个小项目，它的职责就是将网站公布的数据转换为清晰易懂的质量指数，然后发送到社交平台上。目前它有两个帐号，一个在新浪微博（<a href="http://weibo.com/nanjingair">@南京空气质量播报</a>），另一个在Twitter（<a href="https://twitter.com/theNanjingAir">@theNanjingAir</a>）——毕竟最早吸引公众对PM2.5的关注的，便是美使馆在推特上的<a href="http://twitter.com/beijingair">@BeijingAir</a>帐号。</p>

<!--more-->


<h2>官方数据平台协议</h2>

<p>江苏省环境监测中心的发布网站使用了Flex作为前端，使用<a href="http://help.arcgis.com/en/webapi/flex/">ArcGIS API for Flex</a>构建，地图资源来自于<a href="http://www.tianditu.cn/">天地图</a>。Flex跟服务器进行交互最常见的便是WSDL了，经过简单抓包后这一猜想得到了验证。</p>

<p>每一个测控站都有一个唯一的<code>scode</code>，Flex使用Web Service中的<code>getSurvyValue("PM25", scode)</code>来获取相应测控站的PM2.5数据，返回的结果是<code>最近1小时均值：xx微克/立方米;最近24小时均值：xx微克/立方米;xx</code>这样的格式，最后一个<code>xx</code>指的是该条数据的时间，24小时制的小时数。</p>

<h2>空气质量指数（AQI）</h2>

<p>解决了获取数据的问题，当然还需要将其换算成AQI。各个国家有各自的换算方法，不过都大同小异。我国的国标《<a href="http://kjs.mep.gov.cn/pv_obj_cache/pv_obj_id_0D685D84B394517881A5E84FA090CC274B870400/filename/W020120410332725219541.pdf">环境空气质量指数（AQI）技术规定（试行）</a>》（HJ 633—2012）是今年刚发布的，定于2016年1月1日正式实施。在上面的文档中，详细描述了AQI换算的方法，其实AQI是关于监测数据（单位μg/m<sup>3</sup>）的分段线性函数。</p>

<p>准确地说，AQI是将各个检测值的空气质量分指数（IAQI）取最大值得到的，但是本项目中只涉及到了PM 2.5的数据，就暂且以AQI代指PM 2.5的IAQI吧。而且在国标中PM 2.5的1小时实时报是不测算IAQI的，只测算24小时内滑动平均值的指数。不过为了体现实时性，我们可以用同样的方法来计算出1小时的实时指数。</p>

<h3>IAQI计算公式</h3>

<div>
\[ IAQI_P  = \frac {IAQI_{Hi} - IAQI_{Lo}}{ BP_{Hi} - BP_{Lo} }(C_P - BP_{Lo}) + IAQI_{Lo} \]
</div>


<h3>参数含义</h3>

<ul>
<li><code>\( C_P \)</code>：浓度值</li>
<li><code>\( BP_{Hi} \)</code>：分段表中与<script type="math/tex">C_P</script>相近的浓度限值的高位值</li>
<li><code>\( BP_{Lo} \)</code>：分段表中与<script type="math/tex">C_P</script>相近的浓度限值的低位值</li>
<li><code>\( IAQI_{Hi} \)</code>：分段表中与<script type="math/tex">BP_{Hi}</script>对应的指数</li>
<li><code>\( IAQI_{Lo} \)</code>：分段表中与<script type="math/tex">BP_{Lo}</script>对应的指数</li>
</ul>


<h3>分段表</h3>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>IAQI | PM2.5浓度值(μg/m³)
</span><span class='line'>----:|:------------------
</span><span class='line'>  0  |0
</span><span class='line'> 50  |35
</span><span class='line'>100  |75
</span><span class='line'>150  |115
</span><span class='line'>200  |150
</span><span class='line'>300  |250
</span><span class='line'>400  |350
</span><span class='line'>500  |500</span></code></pre></td></tr></table></div></figure>


<h3>计算AQI</h3>

<p>根据上面的公式，我们便可以计算出相应浓度值对应的AQI指数。具体代码就不在文中给出了，项目版本库里都有。</p>

<h3>美标AQI</h3>

<p>有趣的是，国家标准计算AQI的公式跟美国标准的公式是一模一样的，唯一的不同只在分段表中。最初推特上的@BeijingAir计算AQI便是采用的美国标准，这也招来了国内不少人的非议，称国情不同，计算方法也应该不同。</p>

<p>美国国家环境保护局的文件《<a href="http://www.epa.gov/airnow/aqi_tech_assistance.pdf">Technical Assistance Document for the Reporting of Daily Air Quality – the Air Quality Index (AQI)</a> 》(EPA-454/B-09-001)给出了计算AQI的详细方法。他们采用的分段表是这样的：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>AQI      | PM2.5浓度值(μg/m³)
</span><span class='line'>--------:|:------------------
</span><span class='line'>  0-50   |0.0-15.4
</span><span class='line'> 51-100  |15.5-40.4
</span><span class='line'>101-150  |40.5-65.4
</span><span class='line'>151-200  |65.5-150.4
</span><span class='line'>201-300  |150.5-250.4
</span><span class='line'>301-400  |250.5-350.4
</span><span class='line'>401-500  |350.5-500.4</span></code></pre></td></tr></table></div></figure>


<p>聪明的你是不是已经发现区别了？<span class='pullquote-right' data-pullquote='国标计算的AQI在大多数情况下会比美标来的低 '>在高浓度值的区域，国标和美标采用的是相同的分段方式，但是在低浓度的区域，美标明显比国标要严格不少。也就是说，采用国标计算的AQI在大多数情况下会比美标来的低（AQI越低代表空气质量越好）。很显然，<strong>浓度值是非常客观的数据，但是转化成AQI后，一切都变得主观而有趣起来。</strong></p>

<p>如果还不够明白的话，相信下面这张函数图能够说明一切了，呵呵。</span></p>

<p><img src="http://img.dayanjia.com/di/FNI9/aqi.png" title="Concentration to AQI" ></p>

<h2>空气质量指数级别</h2>

<p>有了AQI数据后，我们可以将它进一步模糊化，使用大家都可以理解的文字来大概描述一下空气质量到底怎样。国标采用的是“优”、“良”、“轻度污染”、“中度污染”、“重度污染”、“严重污染”这几个描述，事实上如果出现轻度污染，说明空气质量已经比较糟糕了。</p>

<p>类似的，美标使用的描述有“优秀”、“中等”、“敏感人群有害”、“不健康”、“非常不健康”和“有毒害危险”这六个等级，一一对应。当然，前面说到，美国标准要严格不少，因此有时候国标级别是“良”，而美标级别可能已经达到“不健康”了。</p>

<h2>发布到社交网络</h2>

<p>这个可以算驾轻就熟了，在新浪微博和Twitter上分别申请一个新号，再申请一个API，接着手动OAuth授权一下，拿到Access Token以后就可以用程序发布更新了。</p>

<h2>小插曲：源数据被加密</h2>

<p>这套脚本在官方发布网站的3月30日当天就基本搞定并上线运行了，每隔半个小时更新一次。刚开始官方的数据总是会出现问题，过了一阵子后就正常许多了。不过4月中旬的一天，我突然发现脚本更新不能了。于是手动跑了遍WSDL，发现<code>getSurvyValue</code>的返回变成了Base64编码后的字符串，而且解码后的内容不可读。看来是官方的数据平台进行了升级，对数据做了加密。</p>

<p>这无异于给了我一把锁，而我拿到锁的第一反应便是研究如何打开它。幸运的是，它采用加密算法很快被识破，是基于对称密钥的<a href="http://zh.wikipedia.org/wiki/%E8%B3%87%E6%96%99%E5%8A%A0%E5%AF%86%E6%A8%99%E6%BA%96">DES算法</a>。只要拿到相应的密钥，就能很方便地解开加密的字符串了。</p>

<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML">
</script>


<script type="text/x-mathjax-config">
MathJax.Hub.Config({
    tex2jax: {
        skipTags: ['script', 'noscript', 'style', 'textarea', 'pre'] // removed 'code' entry
    }
});
</script>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[解决Rubypython在Arch Linux下的问题]]></title>
    <link href="http://blog.dayanjia.com/2012/04/fix-rubypython-bug-in-arch-linux/"/>
    <updated>2012-04-18T01:06:00+08:00</updated>
    <id>http://blog.dayanjia.com/2012/04/fix-rubypython-bug-in-arch-linux</id>
    <content type="html"><![CDATA[<p>Octopress使用了Pygments来处理代码高亮。不过从它的名字中我们就能看出，它是Python环境下的项目，本不属于Ruby。好在动态语言具有良好的“粘合性”，在Ruby里面调用Python代码也不是一件难事。Ruby下便有一个名叫<a href="http://rubypython.rubyforge.org/">rubypython</a>的扩展库，它使用了另一个更加底层的，用于将Ruby和其他各种语言进行绑定的<a href="https://github.com/ffi/ffi">FFI</a>库，建立起了Ruby和Python之间的桥梁。</p>

<p>然而，我在Arch Linux下使用Octopress生成高亮代码时，却遇到了意想不到的问题。由于各种扩展库（称为<em>gem</em>）之间依赖关系复杂，加上我对Ruby几乎毫不了解，解决起来绕了不少弯路。</p>

<!--more-->


<h2>问题初现</h2>

<p>当使用<code>rake generate</code>生成站点时，发现报错：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>Building site: source -&gt; public
</span><span class='line'>  File "&lt;string&gt;", line 1
</span><span class='line'>    import sys; print sys.executable
</span><span class='line'>                        ^
</span><span class='line'>SyntaxError: invalid syntax
</span><span class='line'>sh: - : 无效的选项</span></code></pre></td></tr></table></div></figure>


<p>第一反应便是Arch Linux中Python版本的冲突问题，果然被我猜中。</p>

<h2>Arch Linux中的两代Python</h2>

<p>众所周知，Python现在的发展道路是2.x和3.x齐头并进。在Arch的官方源中，自然也同时提供了这两个版本。不过与大多数发行版不同的是，<code>python</code>命令指向的是3.x版本，而要使用2.x版本时就必须用<code>python2</code>命令。事实上，这两个命令都是以符号链接的形式指向了具体版本号的Python：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="nv">$ </span>ls -l /usr/bin | grep python
</span><span class='line'>-rwxr-xr-x 1 root root           150 12月 25 00:37 ipython2
</span><span class='line'>lrwxrwxrwx 1 root root             7 11月 22 01:05 python -&gt; python3
</span><span class='line'>lrwxrwxrwx 1 root root             9  1月 31 21:21 python2 -&gt; python2.7
</span><span class='line'>-rwxr-xr-x 1 root root          6200  1月 31 21:21 python2.7
</span><span class='line'>-rwxr-xr-x 1 root root          1618  1月 31 21:21 python2.7-config
</span><span class='line'>lrwxrwxrwx 1 root root            16  1月 31 21:21 python2-config -&gt; python2.7-config
</span><span class='line'>-rwxr-xr-x 3 root root          6272 11月 22 01:05 python3
</span><span class='line'>-rwxr-xr-x 3 root root          6272 11月 22 01:05 python3.2
</span><span class='line'>lrwxrwxrwx 1 root root            18 11月 22 01:05 python3.2-config -&gt; python3.2mu-config
</span><span class='line'>-rwxr-xr-x 3 root root          6272 11月 22 01:05 python3.2mu
</span><span class='line'>-rwxr-xr-x 1 root root          1821 11月 22 01:05 python3.2mu-config
</span><span class='line'>lrwxrwxrwx 1 root root            16 11月 22 01:05 python3-config -&gt; python3.2-config
</span><span class='line'>lrwxrwxrwx 1 root root            14 11月 22 01:05 python-config -&gt; python3-config
</span></code></pre></td></tr></table></div></figure>


<p>显然Octopress在调用Pygments的时候不会想到这一点，我们需要强制其使用<code>python2.7</code>。</p>

<h2>给Pygments显式指定Python命令行</h2>

<p>在Octopress的<code>plugins</code>目录下，新建一个如下文件：</p>

<div><script src='https://gist.github.com/2407686.js?file='></script>
<noscript><pre><code>require 'pygments'

if !!RUBY_PLATFORM['linux']
    RubyPython.configure :python_exe =&gt; '/usr/bin/python2.7'
end</code></pre></noscript></div>


<h2>一波未平一波又起</h2>

<p>解决了这个问题以后，谁知又出现了新问题。<code>rake generate</code>报出的另一个错误：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>Building site: <span class="nb">source</span> -&gt; public
</span><span class='line'>/home/clippit/.rvm/gems/ruby-1.9.2-p290/gems/rubypython-0.5.3/lib/rubypython.rb:107:in <span class="sb">`</span>start<span class="s1">&#39;: undefined method `Py_IsInitialized&#39;</span> <span class="k">for </span>RubyPython::Python:Module <span class="o">(</span>NoMethodError<span class="o">)</span>
</span><span class='line'>  from /home/clippit/.rvm/gems/ruby-1.9.2-p290/gems/pygments.rb-0.2.11/lib/pygments/ffi.rb:8:in <span class="sb">`</span>start<span class="s1">&#39;</span>
</span><span class='line'><span class="s1"> from /home/clippit/.rvm/gems/ruby-1.9.2-p290/gems/pygments.rb-0.2.11/lib/pygments/ffi.rb:82:in `highlight&#39;</span>
</span><span class='line'>  from /home/clippit/blog/plugins/pygments_code.rb:29:in <span class="sb">`</span>pygments<span class="s1">&#39;</span>
</span><span class='line'><span class="s1"> from /home/clippit/blog/plugins/pygments_code.rb:19:in `highlight&#39;</span>
</span><span class='line'>
</span><span class='line'><span class="c"># (以下 trace 省略)</span>
</span></code></pre></td></tr></table></div></figure>


<p>Google无果后，我决定去看看rubypython的源码，看看到底是什么地方出了问题。</p>

<p>打开到<code>rubypython.rb</code>的107行，的确是<code>RubyPython::Python.Py_IsInitialized</code>这里出现了问题。于是跟踪到<code>python.rb</code>文件，这里便是使用<code>ffi</code>进行语言绑定的核心地带了。看到这样一句话：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="no">EXEC</span> <span class="o">=</span> <span class="no">RubyPython</span><span class="o">::</span><span class="no">PythonExec</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">RubyPython</span><span class="o">.</span><span class="n">options</span><span class="o">[</span><span class="ss">:python_exe</span><span class="o">]</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>于是再转到<a href="https://bitbucket.org/raineszm/rubypython/src/b82963530f97/lib/rubypython/pythonexec.rb"><code>pythonexec.rb</code></a>中。<code>PythonExec</code>类主要是由两个方法构成的，<code>initialize</code>负责寻找系统中python的执行文件路径，而<code>find_python_lib</code>会搜寻相应python的动态库的位置，以便稍候使用<code>ffi</code>进行Python C API的绑定。
<span class='pullquote-right' data-pullquote='这是rubypython的bug '>
Ruby语言的可读性还是很强的，尝试在<code>initialize</code>中插入一句<code>puts @realname</code>，然后再运行一次<code>rake generate</code>，果然看到了打印出来的结果。</p>

<p>但是让人不解的是，输出竟然是<code>/usr/bin/python2.727</code>！作为Ruby小白，通过多次尝试<code>puts</code>和稍微猜测语法含义后，我竟然断定这是rubypython的bug了。想必那段代码是想跟据<code>python</code>找到<code>/usr/bin/python27</code>，不过由于发行版的差异，这在Arch Linux上是错误的。
</span></p>

<p>于是我打算“喂给他”一个<code>python2.727</code>，手动建立一个新的符号链接：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>sudo ln -s /usr/bin/python2.7 /usr/bin/python2.727
</span></code></pre></td></tr></table></div></figure>


<p>再次<code>rake generate</code>，竟然还是报错！</p>

<p>那就继续阅读源码吧，进一步跟踪后发现代码会卡在<code>ffi_lib EXEC.library</code>这里，于是把<code>EXEC.library</code>打印出来，竟然是空的。看来<code>find_python_lib</code>也有问题。毫无头绪之际，在<code>irb</code>中随意输入了几行，竟然发现了问题所在：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="mi">1</span><span class="o">.</span><span class="mi">9</span><span class="o">.</span><span class="mi">2</span><span class="n">p290</span> <span class="p">:</span><span class="mo">015</span> <span class="o">&gt;</span> <span class="k">module</span> <span class="nn">My</span>
</span><span class='line'><span class="mi">1</span><span class="o">.</span><span class="mi">9</span><span class="o">.</span><span class="mi">2</span><span class="n">p290</span> <span class="p">:</span><span class="mo">016</span><span class="o">?&gt;</span>   <span class="kp">extend</span> <span class="no">FFI</span><span class="o">::</span><span class="no">Library</span>
</span><span class='line'><span class="mi">1</span><span class="o">.</span><span class="mi">9</span><span class="o">.</span><span class="mi">2</span><span class="n">p290</span> <span class="p">:</span><span class="mo">017</span><span class="o">?&gt;</span>   <span class="no">EXEC</span> <span class="o">=</span> <span class="no">RubyPython</span><span class="o">::</span><span class="no">PythonExec</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">&quot;python2.7&quot;</span><span class="p">)</span>
</span><span class='line'><span class="mi">1</span><span class="o">.</span><span class="mi">9</span><span class="o">.</span><span class="mi">2</span><span class="n">p290</span> <span class="p">:</span><span class="mo">01</span><span class="mi">8</span><span class="o">?&gt;</span>   <span class="n">ffi_lib</span> <span class="no">EXEC</span><span class="o">.</span><span class="n">library</span>
</span><span class='line'><span class="mi">1</span><span class="o">.</span><span class="mi">9</span><span class="o">.</span><span class="mi">2</span><span class="n">p290</span> <span class="p">:</span><span class="mo">01</span><span class="mi">9</span><span class="o">?&gt;</span>   <span class="k">end</span>
</span><span class='line'><span class="no">LoadError</span><span class="p">:</span> <span class="no">Could</span> <span class="ow">not</span> <span class="nb">open</span> <span class="n">library</span> <span class="s1">&#39;lib.so&#39;</span><span class="p">:</span> <span class="n">lib</span><span class="o">.</span><span class="n">so</span><span class="p">:</span> <span class="n">cannot</span> <span class="nb">open</span> <span class="n">shared</span> <span class="n">object</span> <span class="n">file</span><span class="p">:</span> <span class="no">No</span> <span class="n">such</span> <span class="n">file</span> <span class="ow">or</span> <span class="n">directory</span>
</span><span class='line'>  <span class="n">from</span> <span class="sr">/home/</span><span class="n">clippit</span><span class="o">/.</span><span class="n">rvm</span><span class="o">/</span><span class="n">gems</span><span class="o">/</span><span class="n">ruby</span><span class="o">-</span><span class="mi">1</span><span class="o">.</span><span class="mi">9</span><span class="o">.</span><span class="mi">2</span><span class="o">-</span><span class="n">p290</span><span class="o">/</span><span class="n">gems</span><span class="o">/</span><span class="n">ffi</span><span class="o">-</span><span class="mi">1</span><span class="o">.</span><span class="mi">0</span><span class="o">.</span><span class="mi">11</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">ffi</span><span class="o">/</span><span class="n">library</span><span class="o">.</span><span class="n">rb</span><span class="p">:</span><span class="mi">121</span><span class="ss">:in</span> <span class="sb">`block in ffi_lib&#39;</span>
</span><span class='line'><span class="sb"> from /home/clippit/.rvm/gems/ruby-1.9.2-p290/gems/ffi-1.0.11/lib/ffi/library.rb:88:in `</span><span class="n">map</span><span class="s1">&#39;</span>
</span><span class='line'><span class="s1"> from /home/clippit/.rvm/gems/ruby-1.9.2-p290/gems/ffi-1.0.11/lib/ffi/library.rb:88:in `ffi_lib&#39;</span>
</span><span class='line'>  <span class="n">from</span> <span class="p">(</span><span class="n">irb</span><span class="p">):</span><span class="mi">18</span><span class="ss">:in</span> <span class="sb">`&lt;module:My&gt;&#39;</span>
</span><span class='line'><span class="sb"> from (irb):15</span>
</span><span class='line'><span class="sb"> from /home/clippit/.rvm/rubies/ruby-1.9.2-p290/bin/irb:16:in `</span><span class="o">&lt;</span><span class="n">main</span><span class="o">&gt;</span><span class="err">&#39;</span>
</span></code></pre></td></tr></table></div></figure>


<p>这回的报错让人眼前一亮，恍然大悟——既然我给它<code>python2.727</code>这个文件名，那他就会去找<code>libpython2.727.so</code>，自然还是找不到啊！于是硬着头皮再给系统加一个符号链接</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>sudo ln -s /usr/lib/libpython2.7.so.1.0 /usr/lib/libpython2.727.so
</span></code></pre></td></tr></table></div></figure>


<p>这下世界终于太平了。</p>

<p>奇怪的是，<code>rake generate</code>时却不会显示这个直中要害的信息，而给出了一个很迂回的错误提示，以致于让我绕了很多弯子。这其中究竟是什么原因，实在是不得而知了。</p>

<h2>另一个解决方法</h2>

<p>除了在系统中添加符号链接，还有一个方法，就是Hack掉rubypython的源码。在<code>pythonexec.rb</code>中修改：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='diff'><span class='line'><span class="gu">@@ -19,7 +19,9 @@</span>
</span><span class='line'>
</span><span class='line'>     @dirname = File.dirname(@python)
</span><span class='line'>     @realname = @python.dup
</span><span class='line'><span class="gd">-    if (@realname !~ /#{@version}$/ and @realname !~ /\.exe$/)</span>
</span><span class='line'><span class="gi">+    if (@realname.include? &#39;python2.7&#39;)</span>
</span><span class='line'><span class="gi">+        # Do nothing</span>
</span><span class='line'><span class="gi">+    elsif (@realname !~ /#{@version}$/ and @realname !~ /\.exe$/)</span>
</span><span class='line'>       @realname = &quot;#{@python}#{@version}&quot;
</span><span class='line'>     else
</span><span class='line'>       basename = File.basename(@python, &#39;.exe&#39;)
</span></code></pre></td></tr></table></div></figure>


<p>当然，这个方法非常丑陋。</p>

<h2>参考资料</h2>

<p>后来经过一番搜索，还是有一些相关的资料的，在此列出。</p>

<ul>
<li><a href="http://blog.gonzih.org/blog/2011/09/21/fix-octopress-pygments-error-on-arch-linux/">How to Fix Octopress Pygments Error on Arch Linux</a></li>
<li><a href="https://github.com/github/gollum/issues/225">Py_IsInitialized error</a></li>
<li><a href="http://ruby-china.org/topics/289">解决pygments.rb (RubyPython) 找不到libpython的问题</a></li>
</ul>


<h2>编后</h2>

<p>就在我这这篇文章的时候，rubypython竟然发布新版本(<a href="http://rubygems.org/gems/rubypython">0.6.0</a>)了，真是太巧了。不过暂时还不知道是否修复了这个问题，新版本的代码似乎进行了较大的重构，<code>pythonexec.rb</code>文件被删除，本文所描述的代码转到<code>interpreter.rb</code>中去了。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[从Wordpress迁移到Octopress]]></title>
    <link href="http://blog.dayanjia.com/2012/04/migration-to-octopress-from-wordpress/"/>
    <updated>2012-04-16T20:07:00+08:00</updated>
    <id>http://blog.dayanjia.com/2012/04/migration-to-octopress-from-wordpress</id>
    <content type="html"><![CDATA[<p>这里自从去年5月以来发表了最后一篇文章后，已经停止更新将近一年了。其实这期间一直有再写点文章的欲望，但是每每登录到WordPress的后台，看到那愈发杂乱得不可收拾的网站配置时，就瞬间没有了热情。于是这次终于下了决心，是时候抛弃WordPress拥抱新时代了。</p>

<p>新时代属于自由开放的社群。正所谓，<strong>对于计算机和一切能被用来改造这个世界的事物的使用都不应受到任何限制，任何试图隐藏系统的复杂性的行为都只会得到一个更为复杂的系统</strong>。在WordPress让写日记（博客）这件事变得越来越复杂的时代，不如让我们回归原始吧。抛弃动态网页和数据库的组合，重新拾起静态网页和纯文本的组织，世间一切瞬间变得如此简洁而美丽。</p>

<p>这篇文章是一个新的开始，也顺便说说我在迁移时遇到的一些问题。</p>

<!--more-->


<h2><a href="http://octopress.org/">Octopress</a></h2>

<p>提到Octopress就不得不提到<a href="http://jekyllrb.com/">jekyll</a>。jekyll是一个用ruby写的静态网页生成器，作者是mojombo，Github的创始人之一。事实上，我已经关注jekyll和Octopress很久了，今年帮院里制作的<a href="http://218.94.159.105">十周年院庆网站</a>便用到了jekyll。Octopress基于jekyll，此外提供了一系列开箱即用的插件，外带一套还算不错的主题。</p>

<p>个人觉得，这两个工具还有很大的发展潜力，目前的版本在功能上还是有不少的欠缺的。但是这并不妨碍迁移到这个平台上，况且它们的纯粹性也使得增加新特性或者做Hack更加容易。</p>

<h2>备份</h2>

<h3>备份评论内容</h3>

<p><img class="right" src="http://img.dayanjia.com/di/KLYQ/logo.png"></p>

<p>Octopress由于是纯静态，所以没有办法存储用户评论了，我们可以使用<a href="http://disqus.com/">DISQUS</a>提供的“云评论”服务。首先安装<a href="http://wordpress.org/extend/plugins/disqus-comment-system/">DISQUS的WordPress插件</a>，在插件设置中我们可以将现有的评论内容导入到DISQUS中。DISQUS处理导入数据的时间比较长，往往需要24小时甚至以上的时间。</p>

<h3>备份文章内容</h3>

<p>在WordPress后台我们可以将整站数据备份成一个<code>.xml</code>文件下载下来。同时，我原先文章中的图片都是直接在Wordpress后台上传的，所以要把服务器上<code>wp-content/uploads</code>下的所有文件备份下来。</p>

<h2>切换新站</h2>

<p>大家可能注意到了，现在大眼夹的鸟巢已经换到了新的子域名blog.dayanjia.com上面。感觉这样似乎更加容易管理吧。至于安装和部署Octopress，<a href="http://octopress.org/docs/">官方文档</a>里写的很详细，不再重复了。</p>

<p>原先网站用到的图片，就不麻烦了，还是留在原来的位置吧。之所以上面说要备份，是因为我顺便换了一台VPS（又是一笔迁移的成本啊），好在有<code>scp</code>可以将原先主机上的文件传输到新的主机。</p>

<p>Octopress没有办法上传图片，因此我建立了一个<a href="http://img.dayanjia.com">图床</a>。像我这种写文章喜欢贴图的，今后就要先上传图片到那里，然后链接过来。那个图床似乎任何人都可以上传，如果大家有需要可以借用，不过千万别把我的VPS流量用光了:)</p>

<p>目前的新站我在主机上建立了一个私有Git版本库管理，今后可能会公开到GitHub上面去。</p>

<h2>迁移</h2>

<h3>迁移文章</h3>

<p>jekyll本身提供了一个从WordPress迁移文章的工具，不过对中文实在是不太友好。这里我使用了<a href="http://blog.yorkxin.org/2011/11/26/import-from-wpcom-to-octopress/">YORKXIN</a>的<a href="https://gist.github.com/1394128">修改版本</a>。将上面备份的<code>wordpress.xml</code>放到Octopress根目录，把脚本放到新建的<code>utils</code>目录中，然后运行：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>ruby -r "./utils/wordpressdotcom.rb" -e "Jekyll::WordpressDotCom.process"</span></code></pre></td></tr></table></div></figure>


<p>于是转换好的文章都放进<code>source</code>目录了。</p>

<p>虽然有自动转换工具，但是我之前在用到了很多Wordpress的Short Code，这些东西没办法还是得手工修改。我只修改了最近的十篇文章，更老的内容还没有去检查。毕竟这是一项体力活，或许今后有时间可以慢慢弄。</p>

<p>这次迁移，顺便把文章的分类名字也修改了一下，就是现在导航栏上的那几个成语了。修改文章分类很简单，在<code>source/_posts</code>目录里面批量替换一下就行。</p>

<h3>迁移URL</h3>

<p>虽然以前写了不少中二文章，不过再怎么样也是自己手打出来的，所以还是不忍心抛弃。迁移URL，便是要保证以前的文章链接能够自动重定向到新的链接上。这样既能保证搜索引擎的索引不受影响，也是一项对读者负责任的行为是吧。不过这是一项挺麻烦的事情。</p>

<p>幸好我当初建立WordPress的时候就留下了后路。原先网站的链接是这样的：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>http://dayanjia.com/[year]/[month]/[the-long-long-title].html
</span><span class='line'>http://dayanjia.com/page/xx/
</span><span class='line'>http://dayanjia.com/category/[category-name]/</span></code></pre></td></tr></table></div></figure>


<p>这样的格式是比较容易迁移的。如果原先的文章URL是带有数字ID的话，只能说声抱歉了。到<code>_config.yml</code>里面设置一下新站点的文章链接格式，跟原先的格式保持一致：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='yaml'><span class='line'><span class="l-Scalar-Plain">permalink</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">/:year/:month/:title/</span>
</span><span class='line'><span class="l-Scalar-Plain">category_dir</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">category</span>
</span><span class='line'><span class="l-Scalar-Plain">pagination_dir</span><span class="p-Indicator">:</span>  <span class="c1"># 留空</span>
</span></code></pre></td></tr></table></div></figure>


<p>这里我把文章URL最后的<code>.html</code>去掉了，而且由于改变了分类名称，这里面还需要再做一些工作。</p>

<p>新主机依然使用的Apache，于是到http://dayanjia.com根目录下新建<code>.htaccess</code>文件，加入重写规则，将原先的网址使用301重定向到新的位置：</p>

<figure class='code'><figcaption><span>.htaccess  </span></figcaption>
 <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class='apache'><span class='line'><span class="nb">RewriteEngine</span> <span class="k">On</span>
</span><span class='line'>
</span><span class='line'><span class="nb">RewriteCond</span> %{HTTP_HOST} ^www.dayanjia.com$ [NC]
</span><span class='line'><span class="nb">RewriteRule</span> ^(.*)$ http://dayanjia.com/$1 [R=301,N]
</span><span class='line'>
</span><span class='line'><span class="nb">RewriteBase</span> /
</span><span class='line'><span class="nb">RewriteRule</span> <span class="s2">&quot;^(|index.php)$&quot;</span> http://blog.dayanjia.com/ [R=301,L,NC]
</span><span class='line'><span class="nb">RewriteRule</span> <span class="s2">&quot;^20(.*)\.html$&quot;</span> http://blog.dayanjia.com/20$1/ [R=301,L,NC]
</span><span class='line'><span class="nb">RewriteRule</span> <span class="s2">&quot;^page/(.*)$&quot;</span> http://blog.dayanjia.com/page/$1/ [R=301,L,NC]
</span><span class='line'><span class="nb">RewriteRule</span> ^category/technical-articles$ <span class="s2">&quot;http://blog.dayanjia.com/category/雕虫小技/&quot;</span> [R=301,L,NE,NC]
</span><span class='line'><span class="nb">RewriteRule</span> ^category/released-works$ <span class="s2">&quot;http://blog.dayanjia.com/category/自娱自乐/&quot;</span> [R=301,L,NE,NC]
</span><span class='line'><span class="nb">RewriteRule</span> ^category/comments$ <span class="s2">&quot;http://blog.dayanjia.com/category/一家之言/&quot;</span> [R=301,L,NE,NC]
</span><span class='line'><span class="nb">RewriteRule</span> ^category/(miscellaneous|school-life)$ <span class="s2">&quot;http://blog.dayanjia.com/category/侃侃而谈/&quot;</span> [R=301,L,NE,NC]
</span><span class='line'><span class="nb">RewriteRule</span> ^sitemap.xml$ <span class="s2">&quot;http://blog.dayanjia.com/sitemap.xml&quot;</span> [R=301,L,NE,NC]
</span></code></pre></td></tr></table></div></figure>


<p>稍微解释一下，一开始的两行是为了将带<code>www</code>的请求重定向到不带<code>www</code>上，接下来才是正式的重写。第7行是重定向首页的请求，今后如果要做一个<code>http://dayanjia.com</code>的bio页的话，就需要去掉重定向。第8行是重定向具体文章页面，因为它们都是以<code>20</code>开头的，所以取巧如此匹配了。然后便是重定向分类索引、分类索引和最后的站点地图了。</p>

<p>最后还有一项工作，便是重定向RSS Feed的地址。好在当年我又留了一手，用的是独立的<code>feed.dayanjia.com</code>域名，所以暂时把这个域名的解析301到<code>http://blog.dayanjia.com/atom.xml</code>就行了。</p>

<p>不管怎么说，当年的我还算有点高瞻远瞩吧，留得青山在不怕没柴烧，哈哈哈。</p>

<h3>迁移评论</h3>

<p>既然做好了301，那么迁移评论就显得非常简单了。登录DISQUS后台，进入站点管理后台的“Migrate Threads”栏目，那里有一个“Redirect Crawler”的功能，便是自动跟随301重定向，将评论指向新的网址。点一下那个按钮就大功告成。</p>

<h2>接下来的工作</h2>

<p><span class='pullquote-right' data-pullquote='探索之路还很长。生命不息，折腾不止。'></p>

<p>默认的Octopress主题看上去很大气，不过千篇一律的东西总归会审美疲劳。所以修改甚至换一套主题还是很有必要的。</p>

<p>Ruby平台的工具在Windows下的表现很不尽如人意，我需要在Windows下和Linux下同时自如地使用这套平台，在中文处理和跨脚本语言交互（Octopress使用了Python下的Pygments进行代码高亮）上还需要做一些调整。今后可能单独开一篇文章讲讲这些。</p>

<p>前面说到，Octopress的功能还比较简单，比如标签云没有自带，这一点网上似乎已有解决方案。此外，我还想保持贴图的精神，给每篇文章加一个题头图片，就跟原来在Wordpress的那样。</p>

<p>探索之路还很长。生命不息，折腾不止。</p>

<p></span></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[一次关于互联网传播上非理性暴力的真实体验]]></title>
    <link href="http://blog.dayanjia.com/2011/05/experience-of-irrational-violence-on-internet/"/>
    <updated>2011-05-28T00:00:00+08:00</updated>
    <id>http://blog.dayanjia.com/2011/05/experience-of-irrational-violence-on-internet</id>
    <content type="html"><![CDATA[<p>前两天，有一条“爆炸”性新闻，说<a title="江西抚州市发生连环爆炸 已有5人受伤" href="http://news.163.com/11/0526/14/7504V2BL00014JB5.html" target="_blank">江西抚州三处政府机关大楼被人用炸弹袭击</a>了。听到这个消息，我的脑海里第一时间反映出的是刚刚被奥巴马干掉的<a title="奥萨马·本·拉登" href="http://zh.wikipedia.org/wiki/%E5%A5%A5%E8%90%A8%E9%A9%AC%C2%B7%E6%9C%AC%C2%B7%E6%8B%89%E7%99%BB" target="_blank">本·拉登</a>，不过后来得知这是当地一个<a title="这是一幕长达9年的悲剧" href="http://www.dapenti.com/blog/more.asp?name=xilei&amp;id=44132" target="_blank">被拆迁补偿问题折腾了九年</a>的<a title="钱明奇" href="http://baike.baidu.com/view/5812607.htm" target="_blank">绝望农民</a>对抗国家机器发出的最后一吼。于是我脑海中的印象立即从本拉登变成了<a title="杨佳袭警案" href="http://zh.wikipedia.org/wiki/%E6%9D%A8%E4%BD%B3%E8%A2%AD%E8%AD%A6%E6%A1%88" target="_blank">杨佳</a>，3年前他为了一个“说法”，持刀杀进了上海闸北公安分局。</p>

<p>这两人之所以有联系，恐怕是因为他们最终都选择了一条暴力泄愤的不归路。但是很显然，他们都清楚地明白自己正在做什么，钱明奇甚至在5月9日凌晨的微博上说“当初我没有向唐福珍．汪家正等人舍身保家抗腐那样做，现在更不想做第二个钱云会和徐武，我很想向董存瑞学习”这种现在看来充满了暗示的话语。我们或许可以将这种暴力称作理性暴力。当然这不是今天这篇文章的关注点，只是为了引出所谓的“非理性暴力”而已。简而言之，互联网上的非理性暴力，通俗地用两个字大概描述一下，就是“喷子”。</p>

<!--more-->


<p><a rel="attachment wp-att-1284" href="http://dayanjia.com/wp-content/uploads/2011/05/306851097.png"><img class="alignnone size-large wp-image-1284" title="306851097" src="http://dayanjia.com/wp-content/uploads/2011/05/306851097-580x185.png" alt="" width="580" height="185" /></a></p>

<h2>“喷子”的普遍特征</h2>

<p>互联网中文圈出现所谓“喷子”早就不是一天两天的事情了，可以说各个领域的讨论帖中都能看到这番人群的存在。他们的主要特征有：</p>

<ul>
<li>频繁爆粗口：基本上出口成脏</li>
<li>断章取义，无限放大：往往仅抓住某一点，疯狂挑起争端</li>
<li>优越党：自我感觉良好，占据“制高点”</li>
</ul>


<p>可以说，国内的互联网，水军、五毛、喷子，形成了一道亮丽的风景线。</p>

<h2>一次真实的体验</h2>

<p>上学期，我写了一些自动脚本，定时将学校小百合BBS上的十大帖子内容抓取下来，转发到各个社交网络。这项服务在人人网、新浪微博上还算比较受欢迎。当初我就想，如果仅仅依靠现有的社交网络，感觉似乎太居无定所了，也不利于今后的扩展，于是就有了一个名叫“智汇南大”（<a title="智汇南大" href="http://njulily.com" target="_blank">http://njulily.com</a>）的网站。这其实是用Wordpress搭建的一个站点，脚本通过XML-RPC将更新的内容同步发到该网站上。后来，同步抓取的内容不光包括百合十大，还有学校教务处的通知等，各个社团、学生会这样的组织也可以通过它发布自己的活动信息，由这样的自动脚本同步推送到各大社交网站。</p></p>

<p><a rel="attachment wp-att-1286" href="http://dayanjia.com/wp-content/uploads/2011/05/2011-5-28-15-39-16.png"><img class="alignnone size-large wp-image-1286" title="2011-5-28 15-39-16" src="http://dayanjia.com/wp-content/uploads/2011/05/2011-5-28-15-39-16-580x148.png" alt="" width="580" height="148" /></a></p>

<p>可以说，人人网等社交网络更适合这些内容的生存，以至于主站上的文章存档并没有多少人来点击。不过由于自己写的脚本能够输出百合十大的全文，而BBS提供的RSS只有标题，所以我就在Google Reader订阅了智汇南大，方便自己阅读十大的帖子。同时，我又使用了<a title="Reader2Twitter-Sync Your Google Reader Shared Items to Twitter" href="http://reader2twitter.appspot.com/" target="_blank">Reader2Twitter</a>服务，能将我在阅读器里分享的文章同步到Twitter中去。</p>

<p>本来这一切都和所谓“互联网非理性暴力”毫无关系，直到有一天，可怜的<a title="北京邮电大学校长方滨兴在武大演讲被扔鞋" href="http://www.xj.xinhuanet.com/2011-05/20/content_22814560_1.htm" target="_blank">方校长在武汉大学被人扔了鞋子</a>（<a title="中国防火墙之父遭扔鞋抗议引热议" href="http://www.bbc.co.uk/zhongwen/simp/chinese_news/2011/05/110519_china_fang_binxing.shtml" target="_blank">BBC报道</a>）。后来，在小百合BBS上出现了一篇题为《北郵學生：武汉大学不是传说中的沦落了，而是真的淪落》的帖子（现原帖已被删除）。这篇文章是否真的出自北邮学生不得而知（当时似乎有人说是某北邮学生在人人网上发的日志，现已不可考），不过显然引起了非常大的讨论，在5月19日晚登上十大，随即我在Google Reader中看到这篇文章，进行了分享，然后这条信息被<a title="[阅读分享] [Pictures]北郵學生：武汉大学不是传说中的沦落了，而是真的淪落 http://j.mp/lJg08h 方校长的粉丝粗线了" href="http://twitter.com/#!/clipppit/status/71232197662736384" target="_blank">同步到了Twitter</a>上。</p>

<p><a rel="attachment wp-att-1285" href="http://dayanjia.com/wp-content/uploads/2011/05/2011-5-28-16-01-58.png"><img class="alignnone size-large wp-image-1285" title="2011-5-28 16-01-58" src="http://dayanjia.com/wp-content/uploads/2011/05/2011-5-28-16-01-58-580x252.png" alt="" width="580" height="252" /></a></p>

<p>经过<a title="闲趣" href="http://twitter.com/krafttuc" target="_blank">@krafttuc</a>同学的<a title="写这东西的人是来秀无知的吗" href="http://twitter.com/#!/krafttuc/status/71235698459553792" target="_blank">转发</a>后，这条推被<a title="shizhao" href="http://twitter.com/shizhao" target="_blank">@shizhao</a>转发，他注意到了网站上的一个细节，<a href="http://twitter.com/#!/shizhao/status/71239086106611712" target="_blank">说道</a>“评论已关闭”。</p>

<p><a rel="attachment wp-att-1287" href="http://dayanjia.com/wp-content/uploads/2011/05/2011-5-28-16-07-22.png"><img class="alignnone size-large wp-image-1287" title="2011-5-28 16-07-22" src="http://dayanjia.com/wp-content/uploads/2011/05/2011-5-28-16-07-22-580x257.png" alt="" width="580" height="257" /></a></p>

<p>我和@krafttuc同学在Twitter上不过几百个关注者，而@shizhao则拥有上万的关注者，可以说他是Twitter上的意见领袖之一。经过他的推广，这篇文章和这条链接开始被大量转载，并且出现到国内的新浪微博、腾讯微博、抽屉网等。根据Google Analytics的统计，njulily.com上的<a href="http://njulily.com/post/4419" target="_blank">那篇文章</a>被点击近4000次。有意思的是，从网页点击详情中可以看到，“评论已关闭”旁边的“留下回复”链接竟然被点击了近200次，也就是说，有许多人看到左边的大字后还不甘心，仍然想尝试进行回复。</p>

<p><a rel="attachment wp-att-1288" href="http://dayanjia.com/wp-content/uploads/2011/05/2011-5-28-16-20-33.png"><img class="alignnone size-large wp-image-1288" title="2011-5-28 16-20-33" src="http://dayanjia.com/wp-content/uploads/2011/05/2011-5-28-16-20-33-580x277.png" alt="" width="580" height="277" /></a></p>

<p>这些人发现文章不可回复，便出离愤怒了，有两个人来到了njulily.com的关于页面上，留下了两条评论。以下截图中已经将粗口词汇模糊处理，本图片分级为PG13。</p>

<p><a rel="attachment wp-att-1289" href="http://dayanjia.com/wp-content/uploads/2011/05/njulily.png"><img class="alignnone size-large wp-image-1289" title="njulily" src="http://dayanjia.com/wp-content/uploads/2011/05/njulily-580x269.png" alt="" width="580" height="269" /></a></p>

<p>我算是见识到了信息传播中出现的非理性暴力了，不过还好只有两个“喷子”现身。</p>

<h2>为什么评论已关闭？</h2>

<p>我之前已经说道，那个网站纯粹是为脚本抓取的文章做存档的，因为小百合BBS自有它讨论的地方，为了不让讨论过于分散，我就把抓取来的十大帖子在向Wordpress发布时关闭了评论，并且在文章顶部右侧附上了BBS上原帖的地址。可是就是有人看不到，只管盯着那“评论已关闭”。<strong>我相信@shizhao并不是有意为之，应该是不了解具体情况，但是他一万多的关注者的群体力量瞬间把这桩小事放大了。许多东西经过了一次传播，二次传播，多次传播后，<a title="是什么在影响着信息的准确性" href="http://xianqu.org/2011/05/what-affect-infomation-spread/" target="_blank">往往会跟原始情形相差甚远</a>，想想“拷贝不走样”的游戏便知。</strong>到后来，人们的关注点便转移到了“评论已关闭”上，而不是文章本身，事件本身了。</p>

<p>在互联网上，骂人的成本几乎为零，爆粗口的成本同样也是零，但是这不能作为获取消息不经查证就随意泄愤的理由。当然，还是有不少人了解了njulily.com那个网站的特殊性，并发了<a title="纠正大家一个错误" href="http://twitter.com/#!/bao3/status/71241080078741506" target="_blank">证伪的推</a>，但是证伪的力量永远比不上前者。</p>

<p><a rel="attachment wp-att-1290" href="http://dayanjia.com/wp-content/uploads/2011/05/2011-5-28-16-57-20.png"><img class="alignnone size-large wp-image-1290" title="2011-5-28 16-57-20" src="http://dayanjia.com/wp-content/uploads/2011/05/2011-5-28-16-57-20-580x272.png" alt="" width="580" height="272" /></a></p>

<h2>何处泄愤</h2>

<p>在中国，“评论已关闭”是一个代表着新闻审查和管制的词汇，往往会激发出人们心中的怒火。而人们看到它时，总会有理由愤怒：是不是“<a title="国务院新闻办公室" href="http://www.scio.gov.cn/" target="_blank">有关部门</a>”下了禁令？是不是网站自我审查当缩头乌龟？到头来，人们就会把“评论已关闭”和它们划等号，而不会去想会不会还有其他原因。</p>

<p>无法评论，就无法泄愤，于是各种“喷子”会将自己的无名之火喷向各处。情节轻一点的，会在网站其他可以评论的地方发言，情节重一点的，仿佛看到什么都不顺眼，都会爆一番粗口。话说回来，这种非理性的暴力，究竟是怎么产生的呢？是因为本来可以让人就事论事地发泄的地方被强行关闭，压抑着人们的心理，才产生的吗？</p>

<p>于是再回到文章开头，钱明奇和杨佳并不是头脑发热才去施暴的，这中间几年的功夫，若不是那些能“给说法”的部门推诿责任，不愿态度好一点地解决问题，如此压抑着他们的心理，这样的暴力惨案还会发生吗？</p>

<p>几千年前大禹就知道治理洪水宜疏不宜堵，面对如今，无论是互联网上的洪水，还是现实生活中的猛兽，恐怕总有相似的地方。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[校园里的互联网创新们]]></title>
    <link href="http://blog.dayanjia.com/2011/05/internet-innovation-in-campus/"/>
    <updated>2011-05-04T00:00:00+08:00</updated>
    <id>http://blog.dayanjia.com/2011/05/internet-innovation-in-campus</id>
    <content type="html"><![CDATA[<p>前段时间，有个同学跟我说，他发现了一个叫<a title="点点网| 简单的轻博客社区" href="http://www.diandian.com/" target="_blank">点点</a>的网站，觉得它很不错。我随口说道，其实点点是模仿的国外一个叫<a title="tumblr" href="http://www.tumblr.com" target="_blank">Tumblr</a>的网站，从功能到外观，“抄”得很到位。后来他又提到了<a title="豆瓣FM" href="http://douban.fm/" target="_blank">豆瓣电台</a>，我告诉他，豆瓣电台在建立之初的理念基本上就是模仿美国的<a title="Pandora" href="http://www.pandora.com" target="_blank">Pandora Radio</a>的。听到这些，这位同学感到很失望，说得严重点，就像世界观崩塌了似的——看似国内那些创新的互联网产品，追根刨底竟然都是“抄袭”国外的东西。</p>

<p>所谓“创新”，我们一直在谈，一直在灌输，大到指点江山般的“创新是一个民族的灵魂”，小到学院里、学校里举行的“创新项目”比赛，似乎人人都知道要创新，但是真正的创新在哪里，并没有多少人知道。尽管这样，生活在校园里的人们一直在努力做着尝试。毕竟在学校里，人们总是有着更多奋斗的激情，面对失败所要付出的代价也要小得多。</p>

<!--more-->


<h2>创新和创业</h2>

<p>半个月前，具有<a title="Innovation Works" href="http://www.chuangxin.com/" target="_blank">创新工场</a>背景的点点进入公众视线后，立刻引起了又一轮的关于中国互联网“<a title="Copy to China" href="http://en.wikipedia.org/wiki/Copy_to_China" target="_blank">抄袭之路</a>”的讨论，有人戏称李开复的名字有“<span style="text-decoration: underline;">开</span>始<span style="text-decoration: underline;">复</span>制”的意思，更有人制作了创新工场网站的抄袭品——<a title="Copy Works" href="http://aodaren.com/copy-works/" target="_blank">抄袭工场</a>来讽刺前者。</p>

<p><a rel="attachment wp-att-1256" href="http://dayanjia.com/2011/05/internet-innovation-in-campus.html/diandian"><img class="size-large wp-image-1256" title="点点网截图" src="http://dayanjia.com/wp-content/uploads/2011/05/diandian-580x541.png" alt="" width="580" height="541"/></a></p>

<p><a rel="attachment wp-att-1257" href="http://dayanjia.com/2011/05/internet-innovation-in-campus.html/tumblr"><img class="size-large wp-image-1257" title="老祖宗tumblr截图" src="http://dayanjia.com/wp-content/uploads/2011/05/Tumblr-580x441.png" alt="" width="580" height="441"/></a></p>

<p>“创新”和“创业”只有一字之差，但是它们之间却有说不清道不明的关系。你说两者联系很大吧，的确成功的创业项目一定有它创新的地方。不过话说回来，“创新”是让人们觉得“有趣”“惊喜”，而“创业”却是让人们觉得“有用”“好使”，这么一看，完全不同的概念，似乎联系又不大了。</p>

<h2>创新乎？模仿乎？抄袭乎？</h2>

<p>说实话，“抄袭”是一个非常恶劣的词汇，<strong>在我看来，只有那种剽窃源代码的行为才能算得上抄袭，而根据同一个想法做出的东西，应该叫做仿作</strong>，即模仿品。每每网上有人谈到腾讯，谈到百度，都会有人咬牙切齿地说他们抄袭之类云云。这些谩骂者似乎都在做着世界上最创新的事情似的。（BTW，我个人觉得腾讯最大的诟病是八爪鱼似的疯狂迅速扩张，不给成长中的市场喘息的机会；而百度的最严重问题则是利用搜索引擎赚黑心钱（竞价排名），封闭自己的产品圈（框计算）和极度不尊重版权（百度百科、文库等）。<strong>总之这两家国内最大的互联网企业完全没有做到<a title="Corporate citizenship" href="http://en.wikipedia.org/wiki/Corporate_citizenship" target="_blank">企业公民</a>的角色</strong>。）</p>

<p>模仿是大多数产品、公司成长的必经之路，对于中国互联网的发展现状，充满着模仿作品并不奇怪。况且那些被模仿的东西（大多数都是来自美国），并不是每一个都像Facebook在全世界范围内扩张。例如文章开头说道的Pandora电台，因为版权保护只对美国IP开放收听。再如国内<a title="知乎- 一个真实的网络问答社区，帮助你寻找答案，分享知识 " href="http://www.zhihu.com/" target="_blank">知乎</a>模仿的对象<a title="Quora" href="http://www.quora.com/" target="_blank">Quora</a>明确规定<a title="Does content on Quora need to be written in English?" href="http://www.quora.com/Does-content-on-Quora-need-to-be-written-in-English" target="_blank">提问和回答只能用英语</a>。这时候没有一个本土化的产品来做“模仿者”的角色显然是不可能的。</p>

<p><a rel="attachment wp-att-1264" href="http://dayanjia.com/wp-content/uploads/2011/05/INNOvation_Magazine_Ad_3_by_hany4go10.jpg"><img class="alignnone size-medium wp-image-1264" title="INNOvation_Magazine_Ad_3_by_hany4go10" src="http://dayanjia.com/wp-content/uploads/2011/05/INNOvation_Magazine_Ad_3_by_hany4go10-248x300.jpg" alt="" width="248" height="300" /></a></p>

<p>既然你不能做出一鸣惊人众生皆服之的创新，那么对于国内的模仿者，就应该抱有最起码的尊重，毕竟人家的产品也是自己写一行一行代码堆砌出来的。至于我们什么时候能够拥有真正造福人类的互联网创新，最起码得等到我们能正常无碍地访问代表着当今最前沿互联网科技的那些网站以后（你懂的）。</p>

<h2>校园里的互联网创新</h2>

<p>发现自己好久不写文章，表达能力下降严重，胡扯了半天终于回到标题了。之前说到，大学作为一个试验场，能够规避很多风险，是一块非常好的创新温床。当然像微软、Google、Facebook这样从大学中走出（甚至是从辍学中走出）的成功案例在中国的教育环境下出现的概率不大。但是这并不妨碍广大热血青年奔向未知的理想主义道路。</p>

<p>说实话写这篇文章的想法是来源于太君同学<a title="一些感想 -  SHEIMI'S HIVE" href="http://sheimi.me/2011/04/%E4%B8%80%E4%BA%9B%E6%84%9F%E6%83%B3/" target="_blank">关于学校创新训练计划的感想</a>。学校里、学院里都有组织这些“创新项目”比赛来激发学生的创新思维。<strong>但是事实上这些比赛的项目基本上都是<span style="color: #008000;">已经被证明有效的新技术、新想法的具体实现</span>，真正提出全新想法的可谓凤毛麟角。所谓创新，更多在于“新”，而不是“创”。</strong></p>

<p><a rel="attachment wp-att-1265" href="http://dayanjia.com/wp-content/uploads/2011/05/innovation_nju.jpg"><img class="alignnone size-large wp-image-1265" title="innovation_nju" src="http://dayanjia.com/wp-content/uploads/2011/05/innovation_nju-580x434.jpg" alt="" width="580" height="434" /></a></p>

<p>说到这里，我自己也惭愧地承认下，去年学院的创新杯比赛，我也带队做了一个名为“SweetNS”的类似<a title="HootSuite" href="http://hootsuite.com/" target="_blank">HootSuite</a>的各类社交网络消息同步的Web App，支持国内的诸如人人网、新浪微博、豆瓣等网站，最终也“成功”地获得了1000元奖金。</p>

<p><a rel="attachment wp-att-1266" href="http://dayanjia.com/wp-content/uploads/2011/05/MyTown.jpg"><img class="alignright size-medium wp-image-1266" title="MyTown" src="http://dayanjia.com/wp-content/uploads/2011/05/MyTown-180x300.jpg" alt="" width="180" height="300" /></a></p>

<p>大家都知道，真正的创新是非常难的，有时候生出一个自认为很得意的想法，结果到网上一搜，发现别人早就有了成品，甚至比你的想法更加成熟和完善。开拓未知的领域不是人人都可以办到，但“微创新”就相对易于实现了。今年学院创新杯获奖作品之一的基于LBS的社交游戏MyTown便是个不错的例子。LBS（Location-Based Service）从<a title="foursquare" href="https://foursquare.com/" target="_blank">foursquare</a>开始火起，到国内蜂拥而上的<a title="街旁- Check in your life" href="http://jiepang.com/" target="_blank">街旁</a>、<a title="开开- 记录足迹分享城事" href="http://k.ai/" target="_blank">开开</a>等等模仿者，都是实现了“签到”“当地主”这样的娱乐方式。但是那个项目把大富翁中的虚拟地皮买卖引入LBS，虽然这两者都不是真正的创新，但是相互碰撞后便产生了“微创新”的火花。“微创新”似乎是中国人自创的用来自我安慰的词汇，这个微，究竟微到什么程度便是仁者见仁智者见智的问题了。过于小的微创新，其实应该叫改进，总不能玷污了创新这个词汇吧。</p>

<p>学生总免不了把视线投向学校这样一个特殊的社会环境，事实上这个小环境里的确能推出许多新想法。有一帮清华的学生做了一个叫<a title="同享日程" href="http://tongshare.com/" target="_blank">同享日程</a>的网站，用学号登录后可以看到与自己上同一门课的其他人，进而展开交流。不得不说这个如果作为校园社交的平台，潜力还是很大的。再如国外的一个叫做<a title="LikeALittle" href="http://lal.com" target="_blank">LikeALittle</a>的网站，其实说白了是一个匿名聊天的应用，但是创始人将它的定位巧妙地设定为“在校园里创造浪漫的恋爱气氛”，将匿名发表的内容限定为“调情(flirt)”。这个网站在北美的高校中以病毒式的速度传播开，成立之后的六个星期内已有2000万次页面浏览，现在已经获得了100万美元的投资。</p>

<p><a rel="attachment wp-att-1267" href="http://dayanjia.com/wp-content/uploads/2011/05/Ratatouille.png"><img class="alignleft size-thumbnail wp-image-1267" title="Ratatouille" src="http://dayanjia.com/wp-content/uploads/2011/05/Ratatouille-197x200.png" alt="" width="106" height="108" /></a>电影<a title="美食总动员 Ratatouille" href="http://movie.douban.com/subject/1793491/" target="_blank">《料理鼠王》</a>中有一句名言叫“Anyone Can Cook”，对于任何事物或许都是一样的。所谓的创新，的确很难，但是越是困难就越能激发人的灵感和斗志，越能产生面对它的勇气。校园里的创新们虽然可能在其他人看上去幼稚，不完整成熟，但是这毕竟是第一步。与其浑浑噩噩地荒废大学时光，还不如将灵光乍现的美好付诸实践。《料理鼠王》影片最后评论家<a title="Memorable quotes for Ratatouille" href="http://www.imdb.com/title/tt0382932/quotes?qt=qt0465220" target="_blank">Anton Ego说道</a>：</p>

<blockquote><p>Not everyone can become a great artist, but a great artist can come from anywhere.<br /><br/>并非任何人都能成为伟大的艺术家，但是伟大的艺术家可能来自任何地方。</p></blockquote>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[南京大学体育锻炼刷卡查询 2.0 上线]]></title>
    <link href="http://blog.dayanjia.com/2011/04/physical-exercise-checking-of-nju-search-v2/"/>
    <updated>2011-04-18T00:00:00+08:00</updated>
    <id>http://blog.dayanjia.com/2011/04/physical-exercise-checking-of-nju-search-v2</id>
    <content type="html"><![CDATA[<p>自从上学期搞了这个<a title="体育刷卡次数查询：一次歪打正着的“社会化营销”" href="http://dayanjia.com/2011/01/a-lucky-hit-o.html" target="_blank">查询网站</a>后，似乎有种名声在外的感觉，以至于这个学期一开始就有许多人问我什么时候可以查到刷卡次数。有趣的是，自从上个学期的“拓荒”之后，各路有志者纷纷对体育部的网站提起兴趣，各类刷卡查询网站如雨后春笋一般出现。就我目前知道的，就有<a title="课外锻炼刷卡查询" href="http://usbuild.sinaapp.com/gym/index.php" target="_blank">这个</a>，<a title="南京大学体育锻炼刷卡次数查询 - 开心南大网" href="http://www.happynju.com/plugin.php?id=swipecard:card" target="_blank">这个</a>，还有<a title="体育课外锻炼打卡查询 @ 舍舍网" href="http://gym.sheshewang.com/" target="_blank">这个</a>。于是我就在想，是不是应该弄一个“正统续作”出来呢？好吧，现在续作来了，2.0版也已经上线了。当然，网址还是原来那个：<a title="南京大学体育锻炼刷卡查询 v2.0" href="http://dayanjia.com/gym/" target="_blank">http://dayanjia.com/gym/</a></p>

<!--more-->


<p>其实查询的核心方法还是一样的，这次的2.0版主要加强了前端方面的工作。总是听到有人喊HTML5啊，CSS3啊，其实自己都没有怎么真正接触过，于是这次就稍微使用了一些HTML5的新特性，也玩了玩CSS3。当然，像<a title=" THE PAST, PRESENT &amp; FUTURE OF LOCAL STORAGE FOR WEB APPLICATIONS" href="http://diveintohtml5.org/storage.html" target="_blank">本地存储</a>、<a title=" LET’S CALL IT A DRAW(ING SURFACE)" href="http://diveintohtml5.org/canvas.html" target="_blank">Canvas绘画</a>这些比较高级的特性没有用到，只是尝试了一些新的标签和属性而已。至于CSS3，好玩的特效倒是挺多的，总的来说HTML5+CSS3大大减轻了JavaScript的负担，以前需要用JS写的很多功能，直接就给搞定了，的确很方便。今后倒是可以再开一新帖来讲讲心得体会吧。当然，像AJAX这些东西果断还是需要JS来做的，这次更新我把全站都AJSX化了，其实没几个需要AJAX的地方的。如此一来，所有的操作都不需要重新刷新页面了，是不是有点Web App的感觉呀？</p>

<p>现在HTML5、CSS3最大的问题就是浏览器支持不统一，相信时间可以解决这一切。不妨让我们看看不同的浏览器访问这个网站都是个什么效果吧。</p>

<p><a rel="attachment wp-att-1234" href="http://dayanjia.com/2011/04/physical-exercise-checking-of-nju-search-v2.html/chrome_gym"><img class="size-large wp-image-1234" title="Chrome下，效果很不错，标题是CSS3动画+jQuery动画的结果" src="http://dayanjia.com/wp-content/uploads/2011/04/Chrome_gym-580x496.png" alt="" width="580" height="496" /></a></p>

<p><a rel="attachment wp-att-1237" href="http://dayanjia.com/2011/04/physical-exercise-checking-of-nju-search-v2.html/opera11-10"><img class="size-large wp-image-1237" title="Opera的渲染效果也是不错的，不过没有CSS渐变" src="http://dayanjia.com/wp-content/uploads/2011/04/opera11.10-580x510.png" alt="" width="580" height="510" /></a></p>

<p><a rel="attachment wp-att-1236" href="http://dayanjia.com/2011/04/physical-exercise-checking-of-nju-search-v2.html/ie9_gym"><img class="size-large wp-image-1236" title="IE9不支持文本阴影，也不支持CSS动画，也没有渐变" src="http://dayanjia.com/wp-content/uploads/2011/04/IE9_gym-580x593.png" alt="" width="580" height="593" /></a></p>

<p><a rel="attachment wp-att-1235" href="http://dayanjia.com/2011/04/physical-exercise-checking-of-nju-search-v2.html/firefox_gym"><img class="size-large wp-image-1235" title="Firefox支持的特性很多，不过文字旋转以后渲染效果就有些差了" src="http://dayanjia.com/wp-content/uploads/2011/04/firefox_gym-580x495.png" alt="" width="580" height="495" /></a></p>

<p><a rel="attachment wp-att-1241" href="http://dayanjia.com/2011/04/physical-exercise-checking-of-nju-search-v2.html/cap201104172217"><img class="size-large wp-image-1241" title="Android的浏览器，基于Webkit的，效果尚可" src="http://dayanjia.com/wp-content/uploads/2011/04/CAP201104172217-e1303061912358-580x325.png" alt="" width="580" height="325" /></a></p>

<p><a rel="attachment wp-att-1240" href="http://dayanjia.com/2011/04/physical-exercise-checking-of-nju-search-v2.html/cap201104172213"><img class="size-large wp-image-1240" title="Android下的Opera Mobile，效果比原生浏览器好很多，但是同样没有渐变了" src="http://dayanjia.com/wp-content/uploads/2011/04/CAP201104172213-e1303061783549-580x325.png" alt="" width="580" height="325" /></a></p>

<p><a rel="attachment wp-att-1242" href="http://dayanjia.com/2011/04/physical-exercise-checking-of-nju-search-v2.html/img_0003-2"><img class="size-large wp-image-1242" title="iPad上的Safari，平板就是舒服" src="http://dayanjia.com/wp-content/uploads/2011/04/IMG_0003-450x600.png" alt="" width="450" height="600" /></a></p>

<p><a rel="attachment wp-att-1243" href="http://dayanjia.com/2011/04/physical-exercise-checking-of-nju-search-v2.html/img_0263"><img class="size-large wp-image-1243" title="iPod Touch自然也不会差" src="http://dayanjia.com/wp-content/uploads/2011/04/IMG_0263-400x600.png" alt="" width="400" height="600" /></a></p>

<p><a rel="attachment wp-att-1244" href="http://dayanjia.com/2011/04/physical-exercise-checking-of-nju-search-v2.html/img_0262"><img class="size-large wp-image-1244" title="iPod Touch/iPhone的输入框都是经过特别优化的，只有数字键盘" src="http://dayanjia.com/wp-content/uploads/2011/04/IMG_0262-400x600.png" alt="" width="400" height="600" /></a></p>

<p><a rel="attachment wp-att-1245" href="http://dayanjia.com/2011/04/physical-exercise-checking-of-nju-search-v2.html/cap201104180125"><img class="size-large wp-image-1245" title="Opera Mini浏览其手机版，普通手机的浏览器都会自动跳转到所谓“手机版”，其实就是上个学期的版本的简朴界面" src="http://dayanjia.com/wp-content/uploads/2011/04/CAP201104180125-337x600.png" alt="" width="337" height="600" /></a></p>

<p><a rel="attachment wp-att-1246" href="http://dayanjia.com/2011/04/physical-exercise-checking-of-nju-search-v2.html/screen_shot-1184"><img class="size-large wp-image-1246" title="用Kindle也会自动跳到手机版，这样看似乎页面还行" src="http://dayanjia.com/wp-content/uploads/2011/04/screen_shot-1184-450x600.gif" alt="" width="450" height="600" /></a></p>

<p><a rel="attachment wp-att-1247" href="http://dayanjia.com/2011/04/physical-exercise-checking-of-nju-search-v2.html/screen_shot-1186"><img class="size-large wp-image-1247" title="但是用Kindle的人你真的伤不起呀！！" src="http://dayanjia.com/wp-content/uploads/2011/04/screen_shot-1186-450x600.gif" alt="" width="450" height="600" /></a></p>

<p><a rel="attachment wp-att-1238" href="http://dayanjia.com/2011/04/physical-exercise-checking-of-nju-search-v2.html/ie6_gym"><img class="size-large wp-image-1238" title="最后一张是IE6，没错，你被降级为手机版了！" src="http://dayanjia.com/wp-content/uploads/2011/04/IE6_gym-580x462.png" alt="" width="580" height="462" /></a></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[在PPTP VPN服务器上配置FreeRADIUS+daloRADIUS实现用户跟踪管理]]></title>
    <link href="http://blog.dayanjia.com/2011/03/configure-freeradius-and-daloradius-on-pptp-vpn-server/"/>
    <updated>2011-03-12T00:00:00+08:00</updated>
    <id>http://blog.dayanjia.com/2011/03/configure-freeradius-and-daloradius-on-pptp-vpn-server</id>
    <content type="html"><![CDATA[<p>现在很多拥有国外VPS的朋友都纷纷安装了VPN服务来方便自己上网，有时候我们还会共享出一些帐号给自己的同学、朋友使用。使用VPN来上网、玩网游等能够有效地解决某些线路上的问题，但是用的人一多难免会出现资源分配不均的情况，这时合理的管理手段就显得很有必要了。不过拿常见的PPTP VPN来说，最简单的配置就是使用PPP的<code>chap-secrets</code>文件来静态地保存用户名和密码，这样我们没有办法知道各个用户连接VPN的时间，上传下载的数据量等信息，所谓用户跟踪管理完全就是一笔糊涂账。我们将目光转向一种更加高级的用户验证手段——RADIUS服务，用它就能实现完善的用户跟踪管理功能。</p>

<p>本文以CentOS 5.5操作系统上的PoPToP VPN服务为例讲述<strong>配置FreeRADIUS服务，使用MySQL数据库管理用户验证信息，安装Web管理界面daloRADIUS</strong>的方法，其他VPN例如L2TP、OpenVPN等类似。本文内容参考了诸多资料，恕不一一列出。</p>

<!--more-->


<h2>前置条件</h2>

<p>首先要保证使用<code>chap-secrets</code>验证的PPTP服务能够正常使用。关于配置简单PPTP VPN的方法不在本文的范围之内，请参考<a href="http://www.black-xstar.com/blog/691.html" title="在CentOS下安装PPTP的VPN">这篇文章</a>或其他相关教程。</p>

<p>其次，你需要在服务器上安装好HTTP+PHP+MySQL环境，本例中使用Apache作为HTTP服务器。此外PHP需要安装PEAR。</p>

<h2>科普时间</h2>

<p><strong>PPP</strong>：Point-to-Point Protocol，<a href="%22http://zh.wikipedia.org/wiki/%E7%82%B9%E5%AF%B9%E7%82%B9%E5%8D%8F%E8%AE%AE">点对点协议</a>，是工作在数据链路层的连接协议。常见的ADSL连接时使用的PPPoE便是指的以太网上的点对点协议（Point-to-Point Protocol over Ethernet）。而我们创建连接VPN时也会通过PPP来进行，*nix操作系统上的pppd能够完成这一任务，其进行用户验证的默认方法便是<code>chap-secrets</code>文件。配置完FreeRADIUS后，我们需要把用户验证这一环节交给RADIUS服务器来完成。</p>

<p><strong>RADIUS</strong>：Remote Authentication Dial In User Service，远程用户拨号验证服务，基于<a href="http://www.ietf.org/rfc/rfc2865.txt" title="RFC2865 - Internet Engineering Task Force">RFC2865</a>和<a href="http://www.ietf.org/rfc/rfc2866.txt" title="RFC2866 - Internet Engineering Task Force">RFC2866</a>。具体的工作原理挺复杂的，仔细阅读这两个RFC标准应该可以搞明白。简单的说，它是一个兼顾验证（authentication）、授权（authorization）及记账（accounting）三种服务的协议，即<a href="http://en.wikipedia.org/wiki/AAA_protocol">AAA协议</a>。RADIUS运行在应用层，使用UDP进行传输，它被广泛用于ISP和企业用来控制Internet或内部网络、无线网络的访问。</p>

<p><a href="http://freeradius.org/"><img class="right" src="http://freeradius.org/css/freeradius.png" width="188" height="39" title="'FreeRADIUS Logo'" ></a></p>

<p><a href="http://freeradius.org/"><strong>FreeRADIUS</strong></a>：是一个实现RADIUS协议的软件，基于GPLv2开源。它是目前部署最广泛的开源RADIUS软件。</p>

<p><a href="http://daloradius.com/"><strong>daloRADIUS</strong></a>：是一个FreeRADIUS的Web挂历程序，使用PHP编写。</p>

<h2>安装配置流程</h2>

<h3>配置FreeRADIUS</h3>

<p>[1] 登入终端后，首先安装FreeRAIUS，一般源里两个版本，其中FreeRADIUS 1.x已经不被支持了，我们安装的是freeradius2。</p>

<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>yum install freeradius2 freeradius2-mysql freeradius2-utils
</span></code></pre></td></tr></table></div></figure>


<p>[2] 安装完后，我们编辑<code>/etc/raddb/users</code>，在文件开头加上：<code>testing Cleartext-Password := "password"</code>。</p>

<blockquote><p>Tips：你需要了解如何使用SSH终端，和终端里文本编辑的方法，例如Vim的使用。</p></blockquote>


<p>[3] 启动radiusd，第一次启动会生成密钥，稍等片刻即可。使用<code>-X</code>参数可以让调试信息直接输出屏幕：</p>

<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>radiusd -X
</span></code></pre></td></tr></table></div></figure>


<p>[4] 新打开一个SSH终端，测试服务器是否连通：</p>

<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>radtest testing password 127.0.0.1 0 testing123
</span></code></pre></td></tr></table></div></figure>


<p>如果看到Access-Accept就说明连接成功了。如果看到类似“Ignoring request to authentication address * port 1812 from     unknownclient”的文字，可能需要去修改<code>/etc/raddb/clients.conf</code>，将<code>client localhost</code>段下的<code>ipaddr</code>改为服务器的IP，而不是127.0.0.1。</p>

<p>测试连接成功后，我们可以把<code>users</code>里临时加上去的第一行删除。</p>

<p>[5] 下载ppp源码，因为要用到其中的配置文件：</p>

<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>wget ftp://ftp.samba.org/pub/ppp/ppp-2.4.5.tar.gz
</span><span class='line'>tar zxvf ppp-2.4.5.tar.gz
</span><span class='line'>cp -R /root/ppp-2.4.5/pppd/plugins/radius/etc/ /usr/local/etc/radiusclient
</span></code></pre></td></tr></table></div></figure>


<p>[6] 编辑<code>/usr/local/etc/radiusclient/servers</code>，加上一组服务器和密钥，本例中为“MyVPN”：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>localhost MyVPN</span></code></pre></td></tr></table></div></figure>


<p>[7] 编辑<code>/usr/local/etc/radiusclient/dictionary</code>，将最后一行改为：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>INCLUDE /usr/local/etc/radiusclient/dictionary.microsoft</span></code></pre></td></tr></table></div></figure>


<p>可以再添加一行：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>INCLUDE /usr/local/etc/radiusclient/dictionary.merit</span></code></pre></td></tr></table></div></figure>


<p>[8] 编辑<code>/etc/raddb/clients.conf</code>，把<code>client localhost</code>段下的<code>secret</code>改成刚才指定的密钥。</p>

<p>[9] 编辑<code>/etc/raddb/radiusd.conf</code>，找到<code>$INCLUDE sql.conf</code>，去掉前面的<code>#</code>；找到<code>$INCLUDE sql/mysql/counter.conf</code>，去掉前面的<code>#</code>。</p>

<p>[10] 添加MySQL用户及数据库，你可以使用现成的phpMyAdmin等工具，也可以在终端下操作。本例中，创建了radius的用户和同名的数据库：</p>

<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='sql'><span class='line'><span class="k">CREATE</span> <span class="k">USER</span> <span class="s1">&#39;radius&#39;</span><span class="o">@</span><span class="s1">&#39;localhost&#39;</span> <span class="n">IDENTIFIED</span> <span class="k">BY</span> <span class="s1">&#39;***&#39;</span><span class="p">;</span>
</span><span class='line'><span class="k">CREATE</span> <span class="k">DATABASE</span> <span class="n">IF</span> <span class="k">NOT</span> <span class="k">EXISTS</span> <span class="o">`</span><span class="n">radius</span><span class="o">`</span> <span class="p">;</span>
</span><span class='line'><span class="k">GRANT</span> <span class="k">ALL</span> <span class="k">PRIVILEGES</span> <span class="k">ON</span> <span class="o">`</span><span class="n">radius</span><span class="o">`</span> <span class="p">.</span> <span class="o">*</span> <span class="k">TO</span> <span class="s1">&#39;radius&#39;</span><span class="o">@</span><span class="s1">&#39;localhost&#39;</span><span class="p">;</span>
</span></code></pre></td></tr></table></div></figure>


<p>[11] 编辑<code>/etc/raddb/sql.conf</code>，配置<code>login</code>（用户名），<code>password</code>（密码），<code>radius_db</code>（数据库名）等字段，并找到<code>readclients</code>一行，设为<code>yes</code>并去掉注释符号<code>#</code>。</p>

<p>[12] 编辑<code>/etc/raddb/sites-enabled/default</code>，根据下面的说明注释或取消注释相应的行：</p>

<ul>
<li><code>authorize</code>段，关掉<code>files</code>，打开<code>sql</code>，也可以把<code>unix</code>关掉</li>
<li><code>preacct</code>段，关掉<code>files</code></li>
<li><code>accounting</code>段，打开<code>sql</code>，也可以把<code>unix</code>关掉</li>
<li><code>session</code>段，打开<code>sql</code></li>
<li><code>post-auth</code>段，打开<code>sql</code></li>
<li><code>pre-proxy</code>段，关掉<code>files</code></li>
</ul>


<p>到这一步，我们的FreeRADIUS就算配置好了，用户信息都将保存在MySQL数据库中。至于数据库中的表，我们在后面统一导入。</p>

<h3>配置daloRADIUS</h3>

<p>[13] 首先下载并安装daloRADIUS，其中需要安装一个Pear-DB的包：</p>

<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>wget http://sourceforge.net/projects/daloradius/files/daloradius/daloradius-0.9-8/daloradius-0.9-8.tar.gz
</span><span class='line'>pear install DB
</span><span class='line'>mkdir /usr/share/daloRadius
</span><span class='line'>tar zxvf daloradius-0.9-8.tar.gz
</span><span class='line'>mv daloradius-0.9-8/* /usr/share/daloRadius/
</span><span class='line'>rm -r daloradius-0.9-8
</span></code></pre></td></tr></table></div></figure>


<p>[14] 这时我们将daloRADIUS中附带的sql文件导入MySQL数据库，别忘了输入密码：</p>

<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>mysql -uroot -p radius &lt; /usr/share/daloRadius/contrib/db/fr2-mysql-daloradius-and-freeradius.sql
</span></code></pre></td></tr></table></div></figure>


<p>[15] 编辑<code>/usr/share/daloRadius/library/daloradius.conf.php</code>，这是daloRADIUS的配置文件。首先是MySQL登录信息：</p>

<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='php'><span class='line'><span class="x">$configValues[&#39;CONFIG_DB_HOST&#39;] = &#39;localhost&#39;;</span>
</span><span class='line'><span class="x">$configValues[&#39;CONFIG_DB_USER&#39;] = &#39;radius&#39;;</span>
</span><span class='line'><span class="x">$configValues[&#39;CONFIG_DB_PASS&#39;] = &#39;***&#39;;  // 设为自己的密码 </span>
</span><span class='line'><span class="x">$configValues[&#39;CONFIG_DB_NAME&#39;] = &#39;radius&#39;;</span>
</span></code></pre></td></tr></table></div></figure>


<p>下面有一个daloRADIUS的bug，默认配置中有一个表名和我们导入的不一样，把它改过来：</p>

<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='php'><span class='line'><span class="x">$configValues[&#39;CONFIG_DB_TBL_RADUSERGROUP&#39;] = &#39;radusergroup&#39;;</span>
</span></code></pre></td></tr></table></div></figure>


<p>然后修改daloRADIUS的路径：</p>

<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='php'><span class='line'><span class="x">$configValues[&#39;CONFIG_PATH_DALO_VARIABLE_DATA&#39;] = &#39;/usr/share/daloRadius/var&#39;;</span>
</span></code></pre></td></tr></table></div></figure>


<p>[16] 添加Apache虚拟主机，如果有Web控制面板什么的自然就方便多了，不然就编辑<code>/etc/httpd/conf/httpd.conf</code>，加入：</p>

<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='apache'><span class='line'><span class="nb">Alias</span> <span class="sx">/vpn</span> <span class="s2">&quot;/usr/share/daloRadius/&quot;</span>
</span><span class='line'><span class="nt">&lt;Directory</span> <span class="s">&quot;/usr/share/daloRADIUS&quot;</span><span class="nt">&gt;</span>
</span><span class='line'><span class="nt">&lt;/Directory&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>[17] 重启重启Apache和MySQL：</p>

<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>service httpd restart
</span><span class='line'>service mysqld restart
</span></code></pre></td></tr></table></div></figure>


<p>[18] 打开浏览器，进入daloRADIUS的管理页面（本例中为<code>http://your.domain/vpn</code>），使用默认用户名<code>administrator</code>和密码<code>radius</code>登录。</p>

<p>daloRADIUS似乎写的不怎么样，最新稳定版已经是三年之前的了，不过作者直到现在还在更新SVN，下次有机会可以用最新的SVN版本试试看。在Management中添加一个新用户，注意密码类型选择Cleartext-Password。</p>

<p>[19] 在终端里再次启动<code>radius -X</code>，同时在另一个终端中用<code>radtest username password localhost 0 MyVPN</code>测试一下，看看现在是不是还能正常接通，如果没问题就OK，让我们把这套系统接驳到PPP上。</p>

<h3>配置pppd</h3>

<p>[20] 编辑<code>/etc/ppp/options.pptpd</code>，里面已经有许多配置选项了，我们要保证有下面的几行，如果没有就添加上去，为了保障用户登录的安全我们限制只使用MS-CHAPv2：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>refuse-pap
</span><span class='line'>refuse-chap
</span><span class='line'>refuse-mschap
</span><span class='line'>require-mppe-128
</span><span class='line'>require-mschap-v2</span></code></pre></td></tr></table></div></figure>


<p>在配置文件最后加上3行：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>plugin radius.so
</span><span class='line'>plugin radattr.so
</span><span class='line'>radius-config-file /usr/local/etc/radiusclient/radiusclient.conf</span></code></pre></td></tr></table></div></figure>


<h3>启动服务</h3>

<p>[21] 一切完成后我们不需要使用debug模式启动radiusd了：</p>

<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>service radiusd start
</span></code></pre></td></tr></table></div></figure>


<p>[22] 当然，我们可以把radiusd和pptpd设为开机启动服务：</p>

<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>chkconfig radiusd on
</span><span class='line'>chkconfig pptpd on
</span></code></pre></td></tr></table></div></figure>


<p>至此，PPTP+FreeRADIUS+MySQL+daloRADIUS全部配置完毕，我们在本机上使用添加的用户名和密码拨入VPN，可以正常使用。在daloRADIUS中，还可以看到各个用户每次连接的时长，上传和下载的数据量统计等。daloRADIUS其他的使用方法，本文不再叙述。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[利用reDuh打通HTTP隧道]]></title>
    <link href="http://blog.dayanjia.com/2011/02/use-reduh-create-a-http-tunnel/"/>
    <updated>2011-02-23T00:00:00+08:00</updated>
    <id>http://blog.dayanjia.com/2011/02/use-reduh-create-a-http-tunnel</id>
    <content type="html"><![CDATA[<p>在某些缺乏安全性的Windows服务器上，总会运行着一个Tomcat，上面跑着某些国内外包公司做的缺乏安全性的JSP程序。这些一环套一环的缺乏安全性就造就了一台可怜地服务器，暴露在Internet的某个小角落。但是有时，这些缺乏安全性的服务器会被一面强有力的防火墙保护着，这位忠实的卫士竭尽所能安慰着那千疮百孔的服务器。但是外来的捍卫终究抵不过自身的体弱多病……</p>

<p>说得这么矫情，其实只是假设一个环境。我们现在有一台跑Tomcat的Windows服务器，我们已经能够自由地上传一些文件，执行一些命令行——想必一般的Webshell都能做到这些。这时候我们想获取服务器的终极操控权——远程桌面，却发现3389端口却被防火墙挡住了，这该怎么办呢？</p>

<!--more-->


<h2>准备工作</h2>

<p>当然我们先看一下3389端口是否是开放的，命令行下执行<code>netstat -an</code>，如果能看到类似<code>TCP    0.0.0.0:3389 LISTENING</code>这样的字样就说明很有希望了。然后我们再新建一个用户吧。依次执行<code>net user username password /add</code>和<code>net localgroup administrators username /add</code>。这时候我们满心欢喜地想在机器上远程桌面连接服务器，却发现失败了。排除了一些IP排除策略的可能性后（在注册表中可以检查相关设置），多半就是防火墙的问题了。</p>

<p><img src="http://dayanjia.com/wp-content/uploads/2011/02/mstsc-error.png" width="406" height="272" title="&#34;mstsc error&#34;" alt="&#34;mstsc error&#34;"></p>

<h2>使用reDuh建立隧道</h2>

<p><a href="http://www.sensepost.com/labs/tools/pentest/reduh">reDuh</a>是由国外一个名叫Glenn Wilkinson的安全人员编写的一个通过HTTP协议建立隧道传输各种其他数据的工具。运行于服务器的JSP脚本接受HTTP请求，在本地转发给相应的端口，并接受本地端口的数据再通过HTTP发送给远程客户端。<strong>这样本来应该走其他端口的数据变摇身一变，披上了HTTP协议的报文头，换走HTTP的端口了。</strong>所有HTTP通道中的数据都是经过Base64编码的（Base64可以将二进制数据转换成ASCII字符序列，并且可以解码还原）。下面这张图详细地说明了reDuh的工作流程（图片汉化自<a href="http://www.sensepost.com/cms/resources/labs/conferences/eye_of_the_needle/SensePost_Eye_of_a_Needle.pdf">SensePost&#8217;s BlackHat USA 2008 talk on tunnelling data in and out of networks</a>）：</p>

<p><a href="http://dayanjia.com/wp-content/uploads/2011/02/reDuh-tunnel.png"><img src="http://dayanjia.com/wp-content/uploads/2011/02/reDuh-tunnel-580x404.png" width="580" height="404" title="&#34;reDuh tunnel&#34;" alt="&#34;reDuh tunnel&#34;"></a></p>

<p>将JSP文件上传至服务器后，我们在自己机器上运行客户端建立连接：<code>java -jar reDuhClient.jar http://internal.bigcompany.com/uploads/reDuh.jsp</code>。产生输出：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>[Info]Querying remote web page for usable remote service port
</span><span class='line'>[Info]Remote RPC port chosen as 42000
</span><span class='line'>[Info]Attempting to start reDuh from internal.bigcompany.com/uploads/reDuh.jsp.  Using service port 42000. Please wait...
</span><span class='line'>[Info]reDuhClient service listener started on local port 1010</span></code></pre></td></tr></table></div></figure>


<p>这时候我们登录进本机的1010端口，可以使用telnet或者netcat。登录后我们会看到<code>Welcome to the reDuh command line</code>的提示。输入<code>[createTunnel]1234:127.0.0.1:3389</code>便可以将远程服务器的3389端口和本地的1234端口绑定起来。</p>

<p>这时候我们便可以打开远程桌面连接，连接127.0.0.1:1234，然后就能看到熟悉的界面了。java的控制台会不断输出运行信息。因为绕了很多弯的缘故，所以远程桌面的速度并不快，但是毕竟连接上了，不是吗？</p>

<p><a href="http://dayanjia.com/wp-content/uploads/2011/02/mstsc.png"><img src="http://dayanjia.com/wp-content/uploads/2011/02/mstsc-580x448.png" width="580" height="448"></a></p>

<p>当需要断开连接的时候，我们只需要输入<code>[killReDuh]</code>即可。</p>

<h2>隧道是个好东西</h2>

<p>如果大家知道利用国外服务器的SSH隧道可以用来访问某些被“防火墙”挡住的国外网站的话，一定会觉得这似乎很熟悉。没错，只不过本文是使用的HTTP的80端口，而那是利用的SSH的22端口，采用的协议不同而已。</p>

<p>隧道就是这么个好东西，能够绕过防火墙的限制，在原本不属于自己的道路上跑运输——这就是“隧道”的本意吧？</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[2010年，我的文化娱乐志]]></title>
    <link href="http://blog.dayanjia.com/2011/01/2010-my-entertainment-log/"/>
    <updated>2011-01-06T00:00:00+08:00</updated>
    <id>http://blog.dayanjia.com/2011/01/2010-my-entertainment-log</id>
    <content type="html"><![CDATA[<p>还记得一年前的2010年1月1日，我评选了史上最不具备公信力的“<a title="【年度巨献】2009年度大眼夹选择奖" href="http://dayanjia.com/2010/01/clippit-select-awards-2009.html" target="_blank">大眼夹选择奖</a>”。时间不小心就过去了一年，现在已经是2011年了，话说大眼夹的鸟巢在2010年增加了不到40篇文章，平均一周连一篇都不到啊。看来我距离博客话痨还是有一定距离的，不知道2011年能不能在保证质量的同时增加点数量呢？呵呵……</p>

<p>不管怎样，写点东西记录下2010年吧，这次我选择一个小小的切入点，回顾下2010年的看过听过的电影和音乐吧～</p>

<p><!--more-->
<h2>2010电影篇</h2>
事实上我在2010年看过的当年的电影并不多。作为一个半潜水豆瓣用户，我记录看了40部电影，只有7部是2010年新上映的。那么就回顾一下印象比较深刻的几部吧（排名不分先后）。
<h3 style="clear: both;"><a href="http://movie.douban.com/subject/1858711/"><img class="alignleft" style="float: left;" title="玩具总动员3" src="http://img3.douban.com/mpic/s4217358.jpg" alt="" width="102" height="145" /></a>玩具总动员3</h3>
皮克斯的3D电影，去电影院消费了一场。这部电影真是经历了漫长的等待，从它的首部预告片开始就关注了。《玩具总动员》一代是当年电影技术的里程碑，随后的第二部延续了第一部的好评，<a title="《玩具总动员3》：15年，完美谢幕" href="http://dayanjia.com/2010/06/toy-story-3-perfect-ending-after-15-years.html" target="_blank">到了若干年后的终曲——真是神了啊</a>！怎么说《玩具总动员》也是伴随着这代人一起成长的，必定会有很强的带入感，第三部最后的情节真是催人泪下，现在想想还是很激动啊。同时这部电影也是美国2010年的票房冠军，皮克斯不愧是全球动画电影的翘楚。期待2011年暑期的《汽车总动员2》。
<h3 style="clear: both;"><a href="http://movie.douban.com/subject/3541415/"><img class="alignleft" style="float: left;" title="盗梦空间" src="http://img3.douban.com/mpic/s4356687.jpg" alt="" width="99" height="148" /></a>盗梦空间</h3>
这部电影让中国的观众记住了克里斯托芬·诺兰的名字。他的上一部作品《蝙蝠侠：黑暗骑士》由于涉及到华人海外犯罪的内容未能在国内公映（还是因为有陈冠希的镜头？），但是这部电影彻底把电影局搞审查的那帮人弄晕了，于是迷迷糊糊通过了审查。事实上上面说的《玩具总动员3》其实含有许多宣扬人民推翻专制的台词，电影局的估计看它是动画片就放松了“警惕”哈！《盗梦空间》的剧情紧凑非常精彩，同时也是一部很严谨的电影。
<h3 style="clear: both;"><a href="http://movie.douban.com/subject/1652587/"><img class="alignleft" style="float: left;" title="阿凡达" src="http://img3.douban.com/mpic/s4065701.jpg" alt="" width="99" height="148" /></a>阿凡达</h3>
年初的大片，当时掀起了一阵3D风暴。可惜南京没有IMAX，于是退而求其次在河西的双机3D放映厅看了。画面的确很震撼的啊，除了画面以外，剧情 似乎记不太清楚了。接近年底的时候阿凡达出了一个加长版的，虽然下了1080p的，但是不知道加长了哪些地方，于是也在没有碰过。关于《阿凡达》，可以回 顾我写的两篇日志（<a title="奇妙的世界——《阿凡达》观影心得（上）" href="../2010/01/something-about-the-movie-avatar.html" target="_blank">这里</a>和<a title="奇妙的世界——《阿凡达》观影心得（下）" href="../2010/01/something-about-the-movie-avatar-2.html" target="_blank">那里</a>）。
<h3 style="clear: both;"><a href="http://movie.douban.com/subject/3742360/"><img class="alignleft" style="float: left;" title="让子弹飞" src="http://img3.douban.com/mpic/s4562692.jpg" alt="" width="100" height="148" /></a>让子弹飞</h3>
影片未上映就有许多人力挺这部电影了，于是我就到电影院里面支持了一下姜文。电影的剧情也十分紧凑精彩，不过不知道是投资的问题还是怎么的，电影的画面效果真的不行啊，很多画面能看到很清楚的噪点，那些个特效也很假。联想到电影开映前的佳能DV广告，让人不由得怀疑姜文是不是用的佳能民用DV拍的电影……此外，电影上映以后，有很多人尝试去解读它，毕竟这是由拍过《鬼子来了》这样的被禁电影的，桀骜不驯的姜文拍的，于是就出来了许多“民国8年，9筒老大，带领6个兄弟，干掉黄4郎”这样的说法……搞的像文字狱似的……对此不多作评论……
<h3 style="clear: both;"><a href="http://movie.douban.com/subject/3920977/"><img class="alignleft" style="float: left;" title="迈克尔·杰克逊：就是这样" src="http://img3.douban.com/mpic/s3981043.jpg" alt="" width="99" height="148" /></a>迈克尔·杰克逊：就是这样</h3>
上面的四部电影都是去电影院消费的，下面的就是……嗯，你懂的。这是关于一个神一样的人的纪录片。看完以后你会发现中国大多数的舞台舞美什么的都比不上外国啊，这倒不是说国外的月亮比较圆，事实就摆在这里啊。在这里再次缅怀一下巨星（估计下面的音乐篇里还要再缅怀）。
<h3 style="clear: both;"><a href="http://movie.douban.com/subject/3233761/"><img class="alignleft" style="float: left;" title="宿醉" src="http://img3.douban.com/mpic/s3742526.jpg" alt="" width="97" height="151" /></a>宿醉</h3>
第一看R级猥琐片，算是见识了一下，看过该片后我的口味变得更加广泛了……
<h3 style="clear: both;"><a href="http://movie.douban.com/subject/1291579/"><img class="alignleft" style="float: left;" title="怪兽电力公司" src="http://img3.douban.com/mpic/s3811186.jpg" alt="" width="99" height="148" /></a>怪兽电力公司</h3>
皮克斯补完计划，很早以前看了一半，这回把它看完了，还是1080p的，那两个主角看上去都好有质感啊！一个滑溜溜的，一个毛茸茸的，当然该片不适合用<a title="密集物体恐惧症" href="http://baike.baidu.com/view/1821775.htm" target="_blank">密集恐惧症</a>的观众。
<h3 style="clear: both;"><a href="http://movie.douban.com/subject/3005875/"><img class="alignleft" style="float: left;" title="2012" src="http://img5.douban.com/mpic/s3964805.jpg" alt="" width="99" height="148" /></a>2012</h3>
电脑接在液晶电视上看的，画面倒是挺不错，不过剧情太狗血了，赤裸裸的<a title="主角威能" href="http://zh.wikipedia.org/zh-cn/%E4%B8%BB%E8%A7%92%E5%A8%81%E8%83%BD" target="_blank">主角光环</a>啊！真不愧<a title="NASA声称《2012》是史上最烂科幻片 " href="http://science.solidot.org/article.pl?sid=11/01/04/0314219" target="_blank">被NASA评为最差科幻电影</a>。
<h3 style="clear: both;"><a href="http://movie.douban.com/subject/1304447/"><img class="alignleft" style="float: left;" title="海豚湾" src="http://img3.douban.com/mpic/s4101713.jpg" alt="" width="105" height="141" /></a>海豚湾</h3>
又一部纪录片，拍摄得很大胆，让我不得不佩服那些人为了追随真相敢于放弃一切的信念，我们缺乏的正是这种力量啊！影片最搞笑的是采访那个日本渔政官员，一开始还振振有词的，那个老外给他看了自己iPhone里面他们偷拍的时候后，马上面部僵硬，半天憋出来一句：“你们在哪里拍的？”态度急转直下啊，作为看客表示很过瘾很欢乐啊。
<h3 style="clear: both;"><a href="http://movie.douban.com/subject/1304447/"><img class="alignleft" style="float: left;" title="记忆碎片" src="http://img3.douban.com/mpic/s3185152.jpg" alt="" width="99" height="148" /></a>记忆碎片</h3>
听说《盗梦空间》要上映，于是就拿了诺兰以前的电影看看。这电影把一个本身就很无厘头的故事剪了个稀巴烂，一会儿正放（黑白），一会儿倒放（彩色），直到影片最后黑白和彩色交汇，你才会真正发现这个主角分明就是个神经病啊！影片最细节的地方是在养老院里不到半秒的一个瞬间变化，这是解读剧情的关键。
<h3 style="clear: both;"><a href="http://movie.douban.com/subject/1780330/"><img class="alignleft" style="float: left;" title="致命魔术" src="http://img5.douban.com/mpic/s1872245.jpg" alt="" width="100" height="148" /></a>致命魔术</h3>
于是就又想到了这部电影。电影快结束时，你会想：这就是结局了吧。看了三分钟后，你会想：不是，这才是结局。再看三分钟后，你就会大喊：我去，这TM才是结局啊！
<h3 style="clear: both;"><a href="http://movie.douban.com/subject/1291832/"><img class="alignleft" style="float: left;" title="低俗小说" src="http://img3.douban.com/mpic/s1310511.jpg" alt="" width="97" height="152" /></a>低俗小说</h3>
都说它经典，在我看完记忆碎片后，发现这个片子其实剪得很清晰嘛……相同的人在不同的环境里，可能是一个大好人，也可能是一个大坏蛋——这正是人性的复杂之处。
<h3 style="clear: both;"><a href="http://movie.douban.com/subject/3578981/"><img class="alignleft" style="float: left;" title="叶问2：宗师传奇" src="http://img3.douban.com/mpic/s4208411.jpg" alt="" width="102" height="144" /></a>叶问2：宗师传奇</h3>
前两天发现这部电影在IMDB的年度高分电影中排名第12，似乎很给国产电影长脸啊，不过它在美国可是因为暴力镜头被评为R级的……其实吧这电影很2的，一句“爸爸要练功”，甄子丹的绝世神功就练成了。接下来其实并不是甄子丹在战斗，完全是中华民族之大意识在战斗啊，整个情节就像奥特曼打怪兽似的。
<h3 style="clear: both;"><a href="http://movie.douban.com/subject/3205624/"><img class="alignleft" style="float: left;" title="社交网络" src="http://img5.douban.com/mpic/s4387115.jpg" alt="" width="99" height="148" /></a>社交网络</h3>
年度佳片，扎克伯格太让人崇拜了，不过中国的环境里出不了这种人，还是洗洗睡吧……看看国外的大学一片活跃欢腾，国内大学一片死气沉沉，就知道时代和环境有多不一样了。
<h3 style="clear: both;"><a href="http://movie.douban.com/subject/1292223/"><img class="alignleft" style="float: left;" title="七宗罪" src="http://img3.douban.com/mpic/s3968977.jpg" alt="" width="97" height="152" /></a>七宗罪</h3>
看《社交网络》之前也得温习一下大卫·芬奇的早期作品，这部电影也是拍得很严谨的，虽然说其实我一开始大概就猜到最后的结局了……那个罪犯其实就是个传教士啊。
<h3 style="clear: both;"><a href="http://movie.douban.com/subject/3793023/"><img class="alignleft" style="float: left;" title="三个傻瓜" src="http://img3.douban.com/mpic/s4433349.jpg" alt="" width="101" height="146" /></a>三个傻瓜</h3>
真的，宝莱坞让我刮目相看了。很久没看过这么精彩的片子了，又欢笑又感人的，太给力了。其实印度的大学倒是很中国的高中有几番相似，坚决推荐中国的高中生认真看看这部电影，相信会有很深的触动。可能现实的羁绊会让很多人放弃自己的理想，但是我们仍然要怀有理想，坚持信仰！
<h3 style="clear: both;"><a href="http://movie.douban.com/subject/1292402/"><img class="alignleft" style="float: left;" title="西西里的美丽传说" src="http://img3.douban.com/mpic/s2401991.jpg" alt="" width="100" height="148" /></a>西西里的美丽传说</h3>
我只想说，拍这个电影的小孩太受毒害了……
<h3 style="clear: both;"><a href="http://movie.douban.com/subject/1292267/"><img class="alignleft" style="float: left;" title="银河系漫游指南" src="http://img3.douban.com/mpic/s3109578.jpg" alt="" width="99" height="148" /></a>银河系漫游指南</h3>
很欢乐的一部科幻片，挺恶搞的。可能原版小说在当时那个年代取得的影响的确很大吧，连Google计算器都知道“<a title="the answer to life, the universe, and everything" href="http://www.google.com.hk/search?q=the+answer+to+life%2C+the+universe%2C+and+everything" target="_blank">生命、宇宙和世间万物的终极答案</a>”是42呢。
<h3 style="clear: both;"><a href="http://movie.douban.com/subject/1292271/"><img class="alignleft" style="float: left;" title="毕业生" src="http://img3.douban.com/mpic/s1325574.jpg" alt="" width="101" height="146" /></a>毕业生</h3>
其实我是冲着影片的音乐去看的，说实话对影片本身并没有多少共鸣，恐怕是因为我还没到那个年纪吧～
<h3 style="clear: both;"><a href="http://movie.douban.com/subject/3072124/"><img class="alignleft" style="float: left;" title="玛丽和马克思" src="http://img3.douban.com/mpic/s4034827.jpg" alt="" width="105" height="141" /></a>玛丽和马克思</h3>
两个自闭症患者的交流——实在是太阴暗了，影片的风格很独特，最后结局也很悲。这部电影其实我很早就听说了，不过居然是在“幸福心理学”课上老师放给大家看的。接受这个世界的不完美，爱别人要先爱自己……真正的幸福其实是把握在自己手中的。
<h3 style="clear: both;"><a href="http://movie.douban.com/subject/1292365/"><img class="alignleft" style="float: left;" title="活着" src="http://img3.douban.com/mpic/s1312690.jpg" alt="" width="99" height="149" /></a>活着</h3>
张艺谋早期作品，看完让人感觉这个时代实在是太愚弄人了啊……原版小说写得更悲催，福贵把他身边人全都克死了，电影里好歹还留了几个活口。不过张艺谋后来忙着去拍“三枪”之流的“春晚电影”了，其实中国真的不缺好导演啊，关键是那个悲剧的体制。
<h3 style="clear: both;"><a href="http://movie.douban.com/subject/1291546/"><img class="alignleft" style="float: left;" title="霸王别姬" src="http://img3.douban.com/mpic/s1988198.jpg" alt="" width="99" height="148" /></a>霸王别姬</h3>
陈凯歌早期作品，其实人家不是只会拍“无极”那种水平的电影的，是吧？影片内容很丰满，表现力很强。段小楼和程蝶衣，一对苦命的鸳鸯啊……（掩面逃走）
<h2 style="clear: both;">2010音乐篇</h2>
总有人说我“口味越来越重”，不过说实话这年代所谓“小清新”都开始成文艺装X青年的代表产物了——作为大众主流文化的接受者，什么口味重的都是浮云啊。于是就让我无责任点评下2010的一些专辑吧。
<h3 style="clear: both;"><a href="http://music.douban.com/subject/4140534/"><img class="alignleft" style="float: left;" title="The Fame Monster" src="http://img3.douban.com/mpic/s4019251.jpg" alt="" width="121" height="121" /></a>The Fame Monster</h3>
恐怕Lady Gaga算是所谓“重口味”的代表了吧。其实人家还是很不错的，所谓“雷人”“重口味”往往是炒作出来的啊。这其中Bad Romance、Telephone和Alejandro的MV拍得也很赞啊。不过Telephone的续集啥时候出来呢……
<h3 style="clear: both;"><a href="http://music.douban.com/subject/4823910/"><img class="alignleft" style="float: left;" title="Recovery" src="http://img5.douban.com/mpic/s4374295.jpg" alt="" width="121" height="121" /></a>Recovery</h3>
Eminem的给力专辑啊，虽然我悲剧的英语听力基本听不懂他在说什么、骂什么，但是听着就是很给力啊，哪怕是听着提神也是不错的选择嘛。其中的Love The Way You Lie和Rihanna合唱的，很棒的一首歌，歌词貌似有点悲壮的说。
<h3 style="clear: both;"><a href="http://music.douban.com/subject/4195775/"><img class="alignleft" style="float: left;" title="Animal" src="http://img3.douban.com/mpic/s4149779.jpg" alt="" width="121" height="121" /></a>Animal</h3>
电音十足的“钱婆”Ke$sha，一出来似乎就有人把她和Lady Gaga相提并论了啊。不过她的歌还是口水味多了些，营养不够丰富，用力太过了。话说她那首Tik Tok的MV在iTunes免费下载的时候我还收了一份呢，好歹也是“购买”了一回欧美的正版音乐啊。
<h3 style="clear: both;"><a href="http://music.douban.com/subject/5339942/"><img class="alignleft" style="float: left;" title="The Beginning" src="http://img5.douban.com/mpic/s4548855.jpg" alt="" width="121" height="121" /></a>The Beginning</h3>
黑眼豆豆Black Eyed Peas的上一张专辑叫“结束”，结果这一张就叫“开始”了。风格依旧电音，不过似乎较“结束”水平下滑了。The Time的MV里面出现了黑莓的平板PlayBook，很不错呵呵，话说上次I Gotta Feeling的MV里面是诺基亚的N97啊，黑眼豆豆很与时俱进嘛。
<h3 style="clear: both;"><a href="http://music.douban.com/subject/5293250/"><img class="alignleft" style="float: left;" title="Loud" src="http://img3.douban.com/mpic/s4519626.jpg" alt="" width="121" height="121" /></a>Loud</h3>
来自加勒比海那块一个非常不起眼的小岛的Rihanna的新专辑，总体听上去还是不错的，歌词内容依然很劲爆啊，果断是“家长指导级”。不过有些歌词就比较口水了，比如What&#8217;s My Name。其中有一个Love The Way You Lie 的第二部分。合着她和Eminem商量好了，一首歌放在两个专辑里，你想听全还得买两张专辑啊？
<h3 style="clear: both;"><a href="http://music.douban.com/subject/4856525/"><img class="alignleft" style="float: left;" title="Teenage Dream" src="http://img3.douban.com/mpic/s4419974.jpg" alt="" width="121" height="121" /></a>Teenage Dream</h3>
Katy Perry的眼睛实在是大得吓人啊，不过这专辑貌似只有Teenage Dream、California Gurls和Firework还算不错了，其他的不行。
<h3 style="clear: both;"><a href="http://music.douban.com/subject/5359941/"><img class="alignleft" style="float: left;" title="My Beautiful Dark Twisted Fantasy" src="http://img3.douban.com/mpic/s4518961.jpg" alt="" width="121" height="121" /></a>My Beautiful Dark Twisted Fantasy</h3>
侃爷Kanye West若不是因为<a title="【KANYE WEST VS. TAYLOR SWIFT】" href="http://v.youku.com/v_show/id_XMTE5NDAxNzU2.html" target="_blank">抢了Taylor Swift的话筒</a>，我恐怕还不知道这位知名的制作人呢。整张专辑给我的感觉是配乐非常华丽，听上去很舒服，整体水平比较高。PS，不过专辑封面有点那个啥（这里用的是另外一个版本的封面）……
<h3 style="clear: both;"><a href="http://music.douban.com/subject/4918610/"><img class="alignleft" style="float: left;" title="Speak Now" src="http://img3.douban.com/mpic/s4450893.jpg" alt="" width="121" height="121" /></a>Speak Now</h3>
侃爷似乎很不喜欢乡村歌手，不过乡村风格的音乐似乎很受国内的欢迎。Taylor Swift的新专辑依旧很给力，但是似乎没有特别出众的。
<h3 style="clear: both;"><a href="http://music.douban.com/subject/4151123/"><img class="alignleft" style="float: left;" title="Raymond V Raymond" src="http://img3.douban.com/mpic/s4177902.jpg" alt="" width="121" height="121" /></a>Raymond vs. Raymond</h3>
Usher似乎年纪大了，光顾着培养Justin Bieber去了，不过跳舞依旧给力。不管怎么说不可能回到Yeah那样的辉煌时代了。
<h3 style="clear: both;"><a href="http://music.douban.com/subject/5355138/"><img class="alignleft" style="float: left;" title="Michael" src="http://img3.douban.com/mpic/s4557447.jpg" alt="" width="122" height="121" /></a>Michael</h3>
Michael Jackson的死后“新”专辑，汗，其实这都是人家的遗产了。总体还是非常不错的，尤其那几首Hold My Hand、Much Too Soon，但是似乎没有了他本人的操刀制作，感觉配乐很怪，风格似乎很内敛，不如MJ生前那些歌曲“霸气外露”。我更愿意把这些歌当作Demo版的。
<h3 style="clear: both;"><a href="http://music.douban.com/subject/4906965/"><img class="alignleft" style="float: left;" title="十八般武艺" src="http://img3.douban.com/mpic/s4436491.jpg" alt="" width="121" height="122" /></a>十八般武艺</h3>
王力宏？继续延续上一张《心跳》的无病呻吟颓势，我怎么着都感觉不是十八般武艺，而是黔驴技穷了。
<h3 style="clear: both;"><a href="http://music.douban.com/subject/4820650/"><img class="alignleft" style="float: left;" title="跨时代" src="http://img3.douban.com/mpic/s4364499.jpg" alt="" width="106" height="139" /></a>跨时代</h3>
周杰伦想跨时代，结果步子迈大了，扯着蛋了。廉颇老矣，华语的那帮没啥好的新人出来，全靠老人撑场面，实在是无奈啊。
<h3 style="clear: both;"><a href="http://music.douban.com/subject/5360525/"><img class="alignleft" style="float: left;" title="她说" src="http://img3.douban.com/mpic/s4536209.jpg" alt="" width="131" height="113" /></a>她说</h3>
我始终觉得，华语歌坛的歌手出精选集/自选集就是开始颓废的标志。当然这些歌都还算不错，不过出这样的专辑显得好没诚意啊。
<h3 style="clear: both;"><a href="http://music.douban.com/subject/5385619/"><img class="alignleft" style="float: left;" title="情歌没有告诉你" src="http://img3.douban.com/mpic/s4576452.jpg" alt="" width="121" height="121" /></a>情歌没有告诉你</h3>
不晓得唱片公司怎么想的，这专辑里随便拿一首来当主打估计都比那个要好吧？而且还把人家梁静茹化妆成那样，太颠覆了，吓死爹啊……
<h2 style="clear: both;">2010综艺篇</h2>
<h3 style="clear: both;"><a rel="attachment wp-att-1200" href="http://dayanjia.com/2011/01/2010-my-entertainment-log.html/6b7ce2ec-87e9-4eed-ae4a-5c07685c1a4c"><img class="alignleft size-medium wp-image-1200" style="float: left;" title="38th AMA" src="http://dayanjia.com/wp-content/uploads/2011/01/6B7CE2EC-87E9-4EED-AE4A-5C07685C1A4C-300x168.jpg" alt="" width="300" height="168" /></a>AMA全美音乐奖颁奖典礼</h3>
说实话我这是第一次看国外的这种晚会节目，于是立刻被震撼到了，和CCAV之流搞晚会真的是两个完全不同的感觉。布景、灯光、舞美什么的都是一首歌一个风格，这其中的后台工作估计很大，但是效果真的没话说。不过他们节目居然把过气的后街男孩拿来压轴，无语了。
<h3 style="clear: both;"><a rel="attachment wp-att-1201" href="http://dayanjia.com/2011/01/2010-my-entertainment-log.html/dba9c063-c668-4899-b0bc-007e214676c5"><img class="alignleft size-medium wp-image-1201" style="float: left;" title="JSTV.2010.to.2011.Countdown" src="http://dayanjia.com/wp-content/uploads/2011/01/DBA9C063-C668-4899-B0BC-007E214676C5-300x168.jpg" alt="" width="300" height="168" /></a>江苏卫视跨年演唱会</h3>
这个晚会我还去现场看了一回，虽然位置又偏又高，不过感受感受气氛还算差不多。江苏卫视是国内各种电视台中唯一一个请来了欧美明星的，不过请来的那人我也不熟悉，是所谓“拉丁天后”Shakira，若不是她唱了世界杯的主题曲我估计听都没听说过她。于是夏奇拉的表演就是最有欧美风格的了，她把自己的现场伴奏乐队全都拉过来了，马上和之前的一帮国内歌手就拉开差距了，放伴奏神马的都是浮云啊。之前的国内歌手，一个人抱着个假吉他就能上台唱歌了，现场的观众也就挥挥荧光棒，哪怕是表演非常劲爆的歌曲，台下观众也是不温不火——这一点也和国外的综艺晚会拉开差距了，估计国人比较含蓄吧。还有一点要批评的就是江苏卫视跨年晚会的舞美太垃圾了，估计直接找的南京歌舞团的人排练了两天就上场了，舞美相当于零啊。总的来说，江苏卫视的明星请来了一大把，可是晚会的其他细节做得太欠了。</p>

<p><strong>巴拉巴拉怎么又说了一堆？呵呵，不管怎么祝大家2011年诸事顺利，天天开心啦！</strong></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[体育刷卡次数查询：一次歪打正着的“社会化营销”]]></title>
    <link href="http://blog.dayanjia.com/2011/01/a-lucky-hit-o/"/>
    <updated>2011-01-04T00:00:00+08:00</updated>
    <id>http://blog.dayanjia.com/2011/01/a-lucky-hit-o</id>
    <content type="html"><![CDATA[<p>虽然今年教育部要在全国好几所高校搞所谓“阳光长跑”的活动，<a title="大学生冬季阳光长跑活动：指纹打卡 学生担心个人信息泄露" href="http://www.56.com/u81/v_NTY3MTAzNTA.html" target="_blank">顺便收集下大学生的指纹</a>，但是身在名单上的南京大学似乎并没有贯彻上头的精神，却如火如荼地搞起了“课外体育锻炼刷卡登记”的活动。所谓“体育锻炼刷卡”政策，其实是学校“为了督促学生积极参加课外体育锻炼”而强制大家每天早上和下午进行刷卡“锻炼”登记，整个学期每个人的刷卡计数将会和体育成绩挂钩，小于50次期末最多60分，而大于65次的将会有适量加分。在这里姑且不谈这项政策的合理性和有效性，不过它的确改变了09级和10级学生的生活节奏，每天早上大家赶到校门口刷卡，每天下午开始刷卡前体育馆门口会排起长长的队伍（见题图）。最重要的是，每个人自己刷过几次卡，除非自己记账否则没有办法知道，于是就有了这样一个契机……</p>

<p><!--more-->
<h2>缘起</h2>
11月初的某天晚上，人人网上突然传出一个网址，据说可以在那里查询到自己的刷卡次数。事实上那个网站便是体育部自己的在线办公平台，由于数据采集和上传更新并不是实时的，所以网站上不能准确地查询到截至当前时刻的刷卡次数。于是大家在一边传播这个网址的同时一边在抱怨“我们被学校忽悠”了。</p>

<p>那个网站需要学生用自己的学号和密码（默认和学号相同）登录，然后才可以查到自己的刷卡次数。当我悲剧地发现网站上显示的次数和我实际的刷卡次数相去甚远的时候，同时也看到了那个框架页面其中一块的URL。它是直接将学号作为参数放在URL里面的，我试着退出登录后再次打开这个URL，居然还可以查到自己的刷卡详细信息，换成其他人的学号后输出的便是其他人的刷卡信息。</p>

<p>说实话，这是那个网站在安全和隐私方面的一个低级错误。 <strong>不过，既然我发现了这个，何不将其转化成为同学服务的跳板呢？</strong>
<h2>上线和推广</h2>
于是，在当晚我就用PHP写了一个非常简单的第三方查询网站 ，其工作原理便是将请求转发到那个未严格过滤权限的URL上，将对方结果页面的HTML DOM中提取出关键信息重新展现出来。由于功能很简单，整个PHP+HTML总共只有150行左右。</p>

<p>查询页面非常简洁，只需要输入学号后回车就可以得到相应的刷卡明细和次数统计信息。此外它还支持批量查询，输入两个学号，它会返回这之间所有学号的结果。</p>

<p><a rel="attachment wp-att-1171" href="http://dayanjia.com/2011/01/a-lucky-hit-o.html/gym"><img class="size-medium wp-image-1171" title="查询网站截图（局部）" src="http://dayanjia.com/wp-content/uploads/2011/01/gym-241x300.png" alt="" width="241" height="300" /></a></p>

<p>写这个东西完全是出于好玩，只花了一个晚上就搞定了，于是我将它放到<a title="南京大学体育锻炼刷卡查询" href="http://dayanjia.com/gym" target="_blank">http://dayanjia.com/gym</a>上。第二天晚上，我修复了几个bug，并给它加上了统计代码。</p>

<p>至于推广，我只是那两个晚上在人人网上分别发表了两篇日志，附上了介绍及链接，仅此而已，这毕竟只是出于好玩搞的东西，本身也非常简单。说实话，当时我根本没有想到后面会发生的事情。
<h2>做减法的艺术</h2>
写完这个小东西，我突然觉得“做减法”真挺有意思的。<strong>有时候人们会走向一个极端，就是追求大而全，即所谓的“做加法”。相比之下，做减法显得更有魅力，当然也更加困难。</strong>大家都知道二八理论，但是如何提炼出80%用户需要的那20%功能显然不是那么容易的。做减法做的好的产品，似乎在近几年特别火热。说的广些，<a title="Apple" href="http://www.apple.com" target="_blank">苹果</a>的简洁设计是一种减法，<a title="Twitter" href="http://twitter.com">Twitter</a>的140字限制是一种减法；说的近些，<a title="instagr.am" href="http://instagr.am/" target="_blank">Instagr.am</a>的即时拍照分享是一种减法，<a title="Kik Messenger" href="http://kik.com/" target="_blank">Kik</a>贴近短信的即时通讯模式也是一种减法。</p>

<p>体育部本身的网站上，我们需要先登录系统，然后点若干次鼠标才能得到我们想要的刷卡结果。虽然那网站上还有其他功能，但是学生最最关注的只有刷卡次数统计这一项。<strong>而我重写的刷卡查询网页，只需要输入学号后回车就可以得到结果，虽然不具备其他功能，但是这种功能和操作上的减法，是会受到用户的欢迎的。</strong>
<h2>惊人的访问量</h2>
若不是我放了一个百度统计的代码，我恐怕永远都不会知道这个小小的查询网页会有多么受欢迎。<strong>在上线的不到两个月时间里，访客数（UV）达到了<span style="color: #ff0000;">23000</span>多，PV则接近<span style="color: #ff0000;">88000</span>。</strong>我没有在代码里统计查询的次数，不过估计平均每天有500次查询。学校里参加“刷卡”这项群体性事件的有两个年级，总人数在7000左右。<strong>也就是说，</strong><span style="color: #ff0000;"><strong>平均每个学生在我的查询网站上查询了3次刷卡信息</strong></span><strong>。</strong></p>

<p><a rel="attachment wp-att-1172" href="http://dayanjia.com/2011/01/a-lucky-hit-o.html/baidutongji"><img class="alignnone size-large wp-image-1172" title="baidutongji" src="http://dayanjia.com/wp-content/uploads/2011/01/baidutongji-580x167.png" alt="" width="580" height="167" /></a></p>

<p>页面的访问来源中，有28%的外部链接来源，主要来自于人人网和小百合BBS，有对我那两篇日志的分享，也有直接分享或贴出那个网址的。我在人人网的两篇日志，前一篇被分享449次，浏览1682次，后一篇的数量比较小，浏览仅200余次。在这里我不得不感叹小百合BBS的巨大影响力，哪怕是一个非常不起眼的回复贴中提到了链接，也能带了五六十的访问数量。</p>

<p><a rel="attachment wp-att-1173" href="http://dayanjia.com/2011/01/a-lucky-hit-o.html/lilybbs"><img class="alignnone size-large wp-image-1173" title="lilybbs" src="http://dayanjia.com/wp-content/uploads/2011/01/lilybbs-479x600.png" alt="" width="479" height="600" /></a></p>

<p>此外占绝大多数的是直接流量（几乎没有搜索引擎的流量），<strong>可见有许多人记下了查询网址，或者将它存入了收藏夹/书签中</strong>。当然我们可以看看一些有趣的统计，比如浏览器统计：排名前五的分别是IE8、IE6、IE7、Chrome、搜狗浏览器。</p>

<p><a rel="attachment wp-att-1174" href="http://dayanjia.com/2011/01/a-lucky-hit-o.html/tiyubu"><img class="alignright size-medium wp-image-1174" title="tiyubu" src="http://dayanjia.com/wp-content/uploads/2011/01/tiyubu-300x171.png" alt="" width="300" height="171" /></a>当然，造成如此大访问量的恐怕还有一个因素。体育部的官方查询网站，在我的第三方查询网站上线后两天关闭了学生的登录入口。我咨询了体育部老师后得到的答复是，采集数据和上传统计数据并不是持续实时的，他们生怕学生产生误解，便干脆不让学生登录网站查询。</p>

<p>所幸的是，他们只是关闭了登录入口，而那个免登录查询的URL还可以正常工作，因此这并没有影响到我的查询网站，而这个第三方查询页成为了近两个月里学生知晓自己刷卡情况的唯一途径。
<h2>社会化营销的力量</h2>
我曾经认为SNS一盘散沙的状况其实并不适合进行产品推广之类的活动。但是这次着实让我看到了SNS的力量——仅仅在人人网上发过两篇日志，依靠人们的“分享”和口口相传，它就获得了如此大的关注度。当然这和我的查询网站功能明确实用，面向用户群体清晰有着很大的关系。做好东西是推广的前提，如果你的东西本身很糟，再牛X的社会化营销也是浮云。</p>

<p><strong>事实上，正是由于SNS的去中心化，让每个人都有机会成为焦点，所谓“酒香不怕巷子深”在社会化网络时代得到了最好的诠释。</strong>但是值得注意的是，看访问统计图，为什么前几日每天访问量并不大，在11月17日后便突然上涨了呢？这恐怕离不开人人网上那些“意见领袖”的影响吧。这些人究竟是谁我无从查起，但可以肯定的是，他们拥有一群广泛且热情的关注者。<strong>事实上，若不依靠这些意见领袖的力量，社会化营销的效果可能会很差。</strong></p>

<p>再有一点和以往的网站不同的是，SNS由用户之间的真实生活关系组成。于是这段时间里，<strong>我若是在学校里偶遇有过点头之交的同学，他们总会问我：“那个查刷卡次数的网站是你做的吧？”</strong>相信听到这句话，不管是谁心里都会浮起一丝小小的成就感吧？
<h2>科技改变生活</h2>
<a rel="attachment wp-att-1175" href="http://dayanjia.com/2011/01/a-lucky-hit-o.html/1213666_10835722"><img class="alignleft size-medium wp-image-1175" title="1213666_10835722" src="http://dayanjia.com/wp-content/uploads/2011/01/1213666_10835722-300x225.jpg" alt="" width="300" height="225" /></a>这句口号不知道是谁提出的，但是这句话给我带来的成就感要远远高于上面提到的同学的问话。体育锻炼刷卡这件事本身就已经够“科技改变生活”的了，全校两个年级7000号人跟着政策跑。<span style="color: #999999;">（PS：本文及本人不针对此项政策，也并不针对体育部）</span></p>

<p>但是真正“科技改变生活”的是，在体育部官方查询渠道关闭后，我的第三方查询网站成为了知晓刷卡次数的唯一途径。<strong>仅仅依靠这个简单的查询网站，我就改变了全校7000号人参与刷卡这项活动的心态和习惯。</strong>试想，如果大家都不能准确知道自己的刷卡次数，最终大家的刷卡次数很可能是一个正态分布，但是由于大家可以查到自己的准确次数了，所以最终的刷卡次数就成了双峰分布。</p>

<p><a rel="attachment wp-att-1179" href="http://dayanjia.com/2011/01/a-lucky-hit-o.html/statistics"><img class="alignnone size-large wp-image-1179" title="statistics" src="http://dayanjia.com/wp-content/uploads/2011/01/statistics-580x344.png" alt="" width="580" height="344" /></a></p>

<p>某体育老师调侃道，他们关掉学生的登录入口，就是为了不让大家知道自己的准确次数从而尽量让大家多刷卡，没想到一个第三方的查询网站的出现改变了这一事实，大家全都冲着50次和65次去刷了（前面说到50次和65次是两个指标点），刷够了就不刷了。事实上，根据我的数据监测统计，<strong>刷卡次数为50、51、52、65、66、67次的学生占到了全部参与刷卡人数的50%</strong>！</p>

<p><strong>靠一己之力，改变全校7000人的生活——这样的“科技改变生活”，恐怕才是真正给人带来成就感的源泉吧！</strong></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[给优酷视频的“顶”“踩”自动刷票]]></title>
    <link href="http://blog.dayanjia.com/2010/12/vote-updown-digg-in-youku-automatically/"/>
    <updated>2010-12-14T00:00:00+08:00</updated>
    <id>http://blog.dayanjia.com/2010/12/vote-updown-digg-in-youku-automatically</id>
    <content type="html"><![CDATA[<p>最近一个高中同学说他们学校正在搞一个DV比赛，参赛者把自己的作品上传到优酷，然后其他人在浏览的时候点击“顶”按钮来投票。最终每个DV作品的“顶”的数量将会成为视频评奖的一个依据。于是，某人就心生歹念了，这个“顶”的数量是不是可以刷出来呢？来源于国外著名分享站点Digg的这一创意，实现起来其实很简单，优酷是怎样进行视频的“顶”和“踩”的操作的呢？其实这个所谓比赛没什么大不了的，技术无罪嘛！于是某人的好奇心涌上心头，手一抖就打开了某个优酷视频页面……</p>

<!--more-->


<h2>手动刷票</h2>

<p><img src="http://dayanjia.com/wp-content/uploads/2010/12/2010-12-14-19-56-46.png" width="329" height="254"></p>

<p>在给一个视频进行“顶”或“踩”的操作后，默认就不能再进行操作了。当然，这点小技俩多半是通过cookies来控制的（在中国ADSL盛行的环境下，根据IP来控制实在是荒唐）。我们顶过一个视频后，将浏览器的cookies清空，再刷新一下页面，果然又可以操作了。</p>

<h2>“顶”“踩”背后的HTTP操作</h2>

<p>很显然，这个操作是通过JavaScript进行后台Ajax操作实现的。具体来说，多半是向一个URL发送一个请求，包含了一些参数。想要知道浏览器在访问网页时在背后干了些什么，我们就需要一些抓包工具了。在这里我们使用的是<a href="http://www.fiddler2.com/">Fiddler</a>，一款专门的HTTP debug工具，它仅会抓取HTTP请求的内容，像视频流下载的数据就看不到了，因此抓取结果会清晰很多。</p>

<p>我们打开视频网页，开启Fiddler捕捉，点一下“顶”，我们便可以发现Fiddler中出现了两个结果，其中一个方法为POST，另一个为GET。根据经验，我们自然是更加关注POST方法的请求啦～</p>

<p><a href="http://dayanjia.com/wp-content/uploads/2010/12/2010-12-14-22-01-36.png"><img src="http://dayanjia.com/wp-content/uploads/2010/12/2010-12-14-22-01-36-580x371.png" width="580" height="371"></a></p>

<p>让我们看看Request的内容：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>POST http://v.youku.com/QVideo/~ajax/updown HTTP/1.1
</span><span class='line'>Host: v.youku.com
</span><span class='line'>Connection: keep-alive
</span><span class='line'>Referer: http://v.youku.com/v_playlist/f5376248o1p0.html
</span><span class='line'>Content-Length: 73
</span><span class='line'>Origin: http://v.youku.com
</span><span class='line'>X-Prototype-Version: 1.5.0
</span><span class='line'>X-Requested-With: XMLHttpRequest
</span><span class='line'>Content-type: application/x-www-form-urlencoded; charset=UTF-8
</span><span class='line'>Accept: text/javascript, text/html, application/xml, text/xml, */*
</span><span class='line'>User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.19 Safari/534.13
</span><span class='line'>Accept-Encoding: gzip,deflate,sdch
</span><span class='line'>Accept-Language: zh-CN,zh;q=0.8
</span><span class='line'>Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3
</span><span class='line'>Cookie: isRemoveOnPlayComplete=true; YOUKUSESSID=csrinf00hpfb4u42k7hsfmk7k4; updown_XMjI5NTg4OTM2=1; updown_XMjI5NTc2Nzcy=1; updown_XMjI5NTc1ODQ0=1; PlayListTag=[{"videoid":"57393961","sec":18,"folderid":"5332617","order":"1","pos":"7"}]; wi={ ico_first: 'n5.gif', ico_secend: 'd5.gif', title_first: '%E5%A4%9C%E9%97%B4%3A%E5%B0%8F%E9%9B%AA', title_secend: '%E7%99%BD%E5%A4%A9%3A%E4%B8%AD%E9%9B%AA', phenomenon: '%E5%B0%8F%E9%9B%AA%E8%BD%AC%E4%B8%AD%E9%9B%AA', temperature: '-1%E2%84%83%2F2%E2%84%83', city: '%E5%8D%97%E4%BA%AC' }; __utmarea=103128-20383-3-1; __ysuid=12916513607349bb
</span><span class='line'> 
</span><span class='line'>__ap=%7B%22videoId%22%3A%22XMjI5NjIwNDgw%22%2C%22type%22%3A%22up%22%7D&_=</span></code></pre></td></tr></table></div></figure>


<p>于是一切都一目了然了，POST的Entry中是经过URL Encode的字符串，我们把它解码后便可以得到<code>__ap={"videoId":"XMjI5NjIwNDgw","type":"up"}&amp;amp;_=</code>。很明显吧，我们在点击“顶”的时候，浏览器后台向<code>http://v.youku.com/QVideo/~ajax/updown</code>发送了一个POST请求，请求内容为视频ID和类型（up还是down）。知道了这些，实现脚本自动刷票便不再是难事。</p>

<p>但是我们还有一个疑问，发送请求时cookies是一起发给浏览器的，服务端会不会用这个来判断是否是真实用户投票呢？看来我们还是得从JavaScript下手。</p>

<h2>投票的JavaScript简单分析</h2>

<p>通过简单的查找，我们可以发现优酷实现相关功能的代码在<a href="http://static.youku.com/v1.0.0627/v/js/v4/v4.js">v4.js</a>这个脚本中，不过这个脚本是混淆过的。这个简单，网上有很多JS解混淆的工具（例如<a href="http://jscompress.sinaapp.com/" title="JavaScript(JS) 压缩 / 混淆 / 格式化(美化)">这个</a>）。搞定后，我们可以找到一个updown函数：</p>

<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='js'><span class='line'><span class="nx">updown</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">type</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">if</span> <span class="p">((</span><span class="nx">act</span> <span class="o">=</span> <span class="nx">Nova</span><span class="p">.</span><span class="nx">Cookie</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s2">&quot;updown_&quot;</span> <span class="o">+</span> <span class="nx">videoId2</span><span class="p">))</span> <span class="o">!==</span> <span class="kc">false</span> <span class="o">&amp;&amp;</span> <span class="nx">act</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="nx">Interact</span><span class="p">.</span><span class="nx">showUpDowned</span><span class="p">(</span><span class="nx">act</span><span class="p">)</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>    <span class="nx">Interact</span><span class="p">.</span><span class="nx">updownType</span> <span class="o">=</span> <span class="nx">type</span><span class="p">;</span>
</span><span class='line'>    <span class="nx">Nova</span><span class="p">.</span><span class="nx">QVideo</span><span class="p">.</span><span class="nx">updown</span><span class="p">({</span>
</span><span class='line'>        <span class="nx">videoId</span><span class="o">:</span> <span class="nx">videoId2</span><span class="p">,</span>
</span><span class='line'>        <span class="nx">type</span><span class="o">:</span> <span class="nx">type</span>
</span><span class='line'>    <span class="p">},</span>
</span><span class='line'>    <span class="k">this</span><span class="p">.</span><span class="nx">updownCallback</span><span class="p">)</span>
</span><span class='line'><span class="p">},</span>
</span></code></pre></td></tr></table></div></figure>


<p>可以发现，这个函数其实是调用了<code>Nova.QVideo.updown</code>，囧。这个QVideo的JS是直接写在HTML页面中的：</p>

<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='js'><span class='line'><span class="nx">Nova</span><span class="p">.</span><span class="nx">QVideo</span> <span class="o">=</span> <span class="p">{</span>
</span><span class='line'><span class="nx">_name</span> <span class="o">:</span> <span class="s1">&#39;QVideo&#39;</span><span class="p">,</span>
</span><span class='line'> <span class="p">........................</span>
</span><span class='line'> <span class="nx">updown</span> <span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">param</span><span class="p">,</span> <span class="nx">callback</span><span class="p">,</span> <span class="nx">id</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">nova_call</span><span class="p">(</span><span class="s1">&#39;/QVideo/~ajax/updown&#39;</span><span class="p">,</span> <span class="nx">param</span><span class="p">,</span> <span class="nx">callback</span><span class="p">,</span> <span class="nx">id</span><span class="p">);</span> <span class="p">}</span>
</span><span class='line'><span class="p">};</span>
</span></code></pre></td></tr></table></div></figure>


<p>于是再找到<a href="http://static.youku.com/v1.0.0627/js/nova.js">nova.js</a>，这个<code>nova_call</code>其实是一个辅助函数，它创建一个<code>NovaCall</code>对象，这似乎继承了<a href="http://www.prototypejs.org/" title="Prototype JavaScript framework">Prototype</a>这个JS框架中关于Ajax请求的内容。</p>

<figure class='code'><figcaption><span>NovaCall相关代码  </span></figcaption>
 <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
</pre></td><td class='code'><pre><code class='js'><span class='line'><span class="nx">NovaCall</span> <span class="o">=</span> <span class="nx">Class</span><span class="p">.</span><span class="nx">create</span><span class="p">();</span>
</span><span class='line'><span class="nb">Object</span><span class="p">.</span><span class="nx">extend</span><span class="p">(</span><span class="nb">Object</span><span class="p">.</span><span class="nx">extend</span><span class="p">(</span><span class="nx">NovaCall</span><span class="p">.</span><span class="nx">prototype</span><span class="p">,</span> <span class="nx">Ajax</span><span class="p">.</span><span class="nx">Request</span><span class="p">.</span><span class="nx">prototype</span><span class="p">),</span> <span class="p">{</span>
</span><span class='line'>    <span class="nx">initialize</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">param</span><span class="p">,</span> <span class="nx">callback</span><span class="p">,</span> <span class="nx">id</span><span class="p">,</span> <span class="nx">remote</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="k">this</span><span class="p">.</span><span class="nx">id</span> <span class="o">=</span> <span class="nx">id</span><span class="p">;</span>
</span><span class='line'>        <span class="k">if</span><span class="p">(</span><span class="nx">param</span><span class="o">===</span><span class="kc">undefined</span><span class="p">){</span>
</span><span class='line'>            <span class="nx">param</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Object</span><span class="p">;</span>
</span><span class='line'>        <span class="p">}</span>
</span><span class='line'>        <span class="nx">param</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">param</span><span class="p">);</span>
</span><span class='line'>        <span class="k">if</span><span class="p">(</span><span class="nx">remote</span><span class="o">!=</span><span class="kc">undefined</span><span class="p">){</span>
</span><span class='line'>            <span class="c1">//跨域名版本</span>
</span><span class='line'>            <span class="c1">//callback 必须是字符串</span>
</span><span class='line'>            <span class="kd">var</span> <span class="nx">method</span> <span class="o">=</span> <span class="s1">&#39;get&#39;</span><span class="p">;</span>
</span><span class='line'>            <span class="k">this</span><span class="p">.</span><span class="nx">url</span> <span class="o">=</span> <span class="nx">url</span> <span class="o">+</span> <span class="s1">&#39;?__ap=&#39;</span> <span class="o">+</span><span class="nb">encodeURIComponent</span><span class="p">(</span><span class="nx">param</span><span class="p">)</span> <span class="o">+</span> <span class="s1">&#39;&amp;__ai=&#39;</span> <span class="o">+</span> <span class="nx">id</span> <span class="o">+</span> <span class="s1">&#39;&amp;__callback=&#39;</span> <span class="o">+</span> <span class="nx">callback</span><span class="p">;</span>
</span><span class='line'>            <span class="nx">Nova</span><span class="p">.</span><span class="nx">addScript</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">url</span><span class="p">);</span>
</span><span class='line'>        <span class="p">}</span><span class="k">else</span><span class="p">{</span>
</span><span class='line'>            <span class="kd">var</span> <span class="nx">method</span> <span class="o">=</span> <span class="s1">&#39;post&#39;</span><span class="p">;</span>
</span><span class='line'>            <span class="k">this</span><span class="p">.</span><span class="nx">url</span> <span class="o">=</span> <span class="nx">url</span><span class="p">;</span>
</span><span class='line'>            <span class="k">this</span><span class="p">.</span><span class="nx">callback</span> <span class="o">=</span> <span class="nx">callback</span><span class="p">;</span>
</span><span class='line'>            <span class="k">this</span><span class="p">.</span><span class="nx">transport</span> <span class="o">=</span> <span class="nx">Ajax</span><span class="p">.</span><span class="nx">getTransport</span><span class="p">(</span><span class="nx">url</span><span class="p">);</span>
</span><span class='line'>            <span class="k">this</span><span class="p">.</span><span class="nx">setOptions</span><span class="p">({</span><span class="nx">method</span><span class="o">:</span> <span class="nx">method</span><span class="p">,</span> <span class="nx">parameters</span><span class="o">:</span> <span class="s1">&#39;__ap=&#39;</span><span class="o">+</span><span class="nb">encodeURIComponent</span><span class="p">(</span><span class="nx">param</span><span class="p">)});</span>
</span><span class='line'>            <span class="k">this</span><span class="p">.</span><span class="nx">options</span><span class="p">.</span><span class="nx">onComplete</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">recv</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
</span><span class='line'>            <span class="k">this</span><span class="p">.</span><span class="nx">options</span><span class="p">.</span><span class="nx">onFailure</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">error</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
</span><span class='line'>            <span class="k">this</span><span class="p">.</span><span class="nx">request</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">url</span><span class="p">);</span>
</span><span class='line'>        <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="p">},</span>
</span><span class='line'>    <span class="nx">recv</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">trans</span><span class="p">,</span> <span class="nx">obj</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">obj</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>            <span class="k">try</span> <span class="p">{</span> <span class="nx">obj</span> <span class="o">=</span> <span class="nb">eval</span><span class="p">(</span><span class="s1">&#39;(&#39;</span><span class="o">+</span><span class="nx">trans</span><span class="p">.</span><span class="nx">responseText</span><span class="o">+</span><span class="s1">&#39;)&#39;</span><span class="p">);</span> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{}</span>
</span><span class='line'>        <span class="p">}</span>
</span><span class='line'>        <span class="k">this</span><span class="p">.</span><span class="nx">callback</span><span class="p">(</span><span class="nx">obj</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">id</span><span class="p">);</span>
</span><span class='line'>    <span class="p">},</span>
</span><span class='line'>    <span class="nx">error</span><span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>        <span class="k">if</span> <span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">nova_error_hook</span><span class="p">)</span> <span class="nb">window</span><span class="p">.</span><span class="nx">nova_error_hook</span><span class="p">();</span>
</span><span class='line'>    <span class="k">else</span> <span class="nx">Nova</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Error in transport.&#39;</span><span class="p">);</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">});</span>
</span></code></pre></td></tr></table></div></figure>


<p>仔细看看，它竟然没有主动发送有关cookies的信息，看来我多虑了。绕了半天，我们也总算看出了“顶”和“踩”的内部实现方式，也加深了我们对其的理解。</p>

<h2>Python实现自动刷票</h2>

<p>于是我们便可以使用Python中的<code>urllib2</code>来自动刷票了，模拟HTTP请求即可。为了增加“真实性”，我们在请求头部加了诸如User-Agent这几个参数。友情提醒：以下代码仅供参考。</p>

<figure class='code'><figcaption><span>vote.py </span></figcaption>
 <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
</pre></td><td class='code'><pre><code class='py'><span class='line'><span class="c">#!/usr/bin/python</span>
</span><span class='line'><span class="c"># -*- coding:utf-8 -*-</span>
</span><span class='line'>
</span><span class='line'><span class="kn">import</span> <span class="nn">urllib2</span><span class="o">,</span> <span class="nn">time</span>
</span><span class='line'>
</span><span class='line'><span class="n">url</span> <span class="o">=</span> <span class="s">&#39;http://v.youku.com/QVideo/~ajax/updown&#39;</span>
</span><span class='line'><span class="n">param</span> <span class="o">=</span> <span class="s">&#39;__ap=%7B%22videoId</span><span class="si">%22%</span><span class="s">3A</span><span class="si">%22X</span><span class="s">MjI5NjIwNDgw</span><span class="si">%22%</span><span class="s">2C%22type</span><span class="si">%22%</span><span class="s">3A</span><span class="si">%22u</span><span class="s">p</span><span class="si">%22%</span><span class="s">7D&amp;_=&#39;</span>
</span><span class='line'><span class="n">headers</span> <span class="o">=</span> <span class="p">{</span><span class="s">&#39;Referer&#39;</span><span class="p">:</span> <span class="s">&#39;http://v.youku.com/v_playlist/f5376248o1p0.html&#39;</span><span class="p">,</span>
</span><span class='line'>           <span class="s">&#39;x-prototype-version&#39;</span><span class="p">:</span> <span class="s">&#39;1.5.0&#39;</span><span class="p">,</span>
</span><span class='line'>           <span class="s">&#39;x-requested-with&#39;</span><span class="p">:</span> <span class="s">&#39;XMLHttpRequest&#39;</span><span class="p">,</span>
</span><span class='line'>           <span class="s">&#39;User-Agent&#39;</span><span class="p">:</span> <span class="s">&#39;Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C)&#39;</span><span class="p">,</span>
</span><span class='line'>           <span class="s">&#39;Content-Type&#39;</span><span class="p">:</span> <span class="s">&#39;application/x-www-form-urlencoded; charset=UTF-8&#39;</span><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="n">request</span> <span class="o">=</span> <span class="n">urllib2</span><span class="o">.</span><span class="n">Request</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">param</span><span class="p">,</span> <span class="n">headers</span><span class="p">)</span>
</span><span class='line'><span class="n">i</span> <span class="o">=</span> <span class="mi">1</span>
</span><span class='line'><span class="k">while</span> <span class="n">i</span><span class="o">&lt;</span><span class="mi">300</span><span class="p">:</span>
</span><span class='line'>    <span class="n">response</span> <span class="o">=</span> <span class="n">urllib2</span><span class="o">.</span><span class="n">urlopen</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
</span><span class='line'>    <span class="k">print</span> <span class="s">&#39;</span><span class="si">%s</span><span class="s"> Count: </span><span class="si">%s</span><span class="s">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">read</span><span class="p">(),</span> <span class="n">i</span><span class="p">,)</span>
</span><span class='line'>    <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
</span><span class='line'>    <span class="n">i</span> <span class="o">+=</span> <span class="mi">1</span>
</span></code></pre></td></tr></table></div></figure>


<p><img src="http://dayanjia.com/wp-content/uploads/2010/12/2010-12-14-22-32-40.png" width="357" height="363"></p>

<p>最后声明一下，本文仅供学习研究，请不要用于恶意用途哦～以上Python代码基于GPL v3发布。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[就今天下午域名解析错误的事故向大家道歉]]></title>
    <link href="http://blog.dayanjia.com/2010/12/apology-for-domain-error/"/>
    <updated>2010-12-09T00:00:00+08:00</updated>
    <id>http://blog.dayanjia.com/2010/12/apology-for-domain-error</id>
    <content type="html"><![CDATA[<p>事情的缘由是这样的：我想在我的VPS增加IPv6的支持，于是申请了HE的IPv6 Tunnel。配置好IPv6的地址，并且给dayanjia.com域名增加了AAAA记录后，我发现在IPv6环境下访问http://dayanjia.com并没有返回正确的内容。于是我就开始折腾服务器上的Apache，不知道怎么弄的给搞定了，IPv6下正常访问了，但是却忽略了IPv4的环境下，该域名被指向我服务器上的另一个网站（一个正在开发的项目的一部分，汗-_-|||）。由于本人的疏忽，忘记了广大的IPv4群众，由于后来去上课了所以一个下午没发现。回来后发现博客的RSS突然多了许多另一个网站上的文章，才发现问题。</p>

<p><strong>在此向各位郑重道歉</strong>，相信大家看到RSS输出的内容会感到很诡异的，呵呵。再次感谢您对大眼夹的鸟巢的支持！</p>

<p>PS：现在恢复了原本的情况，IPv6下仍然无法访问网站，有哪位朋友知道如何正确配置的烦请告知，感激不尽！</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[自动获取带有防盗链保护的图片并在浏览器中显示]]></title>
    <link href="http://blog.dayanjia.com/2010/12/fetch-anti-stealing-linked-images-and-show-them-in-browser-automatically/"/>
    <updated>2010-12-03T00:00:00+08:00</updated>
    <id>http://blog.dayanjia.com/2010/12/fetch-anti-stealing-linked-images-and-show-them-in-browser-automatically</id>
    <content type="html"><![CDATA[<p>许多网站或者处于保护资源的目的，或者处于节省服务器资源的考虑，将其下的图片等多媒体资源设置了防盗链保护，只有从原始网站访问时才会正确显示。若是有人将这些图片外链到其他地方，就会无法显示，或者显示一个开发者预先指定好的防盗链替代图片。当我们想要真正获取这些图片的时候，就会遇到一些麻烦。当然，麻烦总是能被解决的，今天就来结合最近的一个项目来看看如何做到自动获取这些图片，并在本地进行缓存，同时发送给浏览器显示。</p>

<!--more-->


<p>以南京大学小百合BBS为例，我们已经使用脚本实现了获取某帖子中楼主贴的所有信息并且转换成了干净的HTML。这时候所有的图片都是以这样的形式呈现的：</p>

<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='html'><span class='line'><span class="nt">&lt;img</span> <span class="na">src=</span><span class="s">&quot;http://bbs.nju.edu.cn/XXXXXXXXXXXX&quot;</span> <span class="na">alt=</span><span class="s">&quot;&quot;</span> <span class="nt">/&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>这时我们将这些HTML发布到其他网站时，图片全部显示为一张带有来源说明的防盗链图片，浏览效果很差。</p>

<h2>Referer：防盗链的关键</h2>

<p>事实上，大多数网站判断访问来源是通过HTTP Request Header中的Referer判断的。浏览器访问资源时，会自动附带上这个Referer字段表示用户是从那个网址访问到该资源的。在<a href="http://www.ietf.org/rfc/rfc2616.txt" title="Hypertext Transfer Protocol -- HTTP/1.1">RFC 2616 超文本传输协议 HTTP/1.1</a>中，有对它的详细描述。</p>

<p>当我们从外站访问这些图片时，浏览器自动在Header中Referer字段提供了当前的网址，那么对方服务器一判断，不是从自己网站访问的，自然就拒绝显示了。</p>

<h2>curl：自由获取任意资源</h2>

<p>为了破解这种限制，自然要请来强大的curl。这里我们使用的是PHP自带的curl库。在PHP中使用curl，基本上分为三步，首先<code>curl_init</code>初始化一个连接，然后用<code>curl_setopt</code>指定连接的各种操作和属性，最后用<code>curl_exec</code>执行。让我们来看具体代码：</p>

<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class='php'><span class='line'><span class="k">function</span> <span class="nf">fetch_bbs_image</span><span class="p">(</span><span class="nv">$url</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="nv">$curl</span> <span class="o">=</span> <span class="nb">curl_init</span><span class="p">(</span><span class="nv">$url</span><span class="p">);</span> <span class="c1">//初始化</span>
</span><span class='line'>    <span class="nb">curl_setopt</span><span class="p">(</span><span class="nv">$curl</span><span class="p">,</span> <span class="nx">CURLOPT_HEADER</span><span class="p">,</span> <span class="k">FALSE</span><span class="p">);</span>
</span><span class='line'>    <span class="c1">//将结果输出到一个字符串中，而不是直接输出到浏览器</span>
</span><span class='line'>    <span class="nb">curl_setopt</span><span class="p">(</span><span class="nv">$curl</span><span class="p">,</span> <span class="nx">CURLOPT_RETURNTRANSFER</span><span class="p">,</span> <span class="k">TRUE</span><span class="p">);</span>
</span><span class='line'>    <span class="c1">//最重要的一步，手动指定Referer</span>
</span><span class='line'>    <span class="nb">curl_setopt</span><span class="p">(</span><span class="nv">$curl</span><span class="p">,</span> <span class="nx">CURLOPT_REFERER</span><span class="p">,</span> <span class="s1">&#39;http://bbs.nju.edu.cn&#39;</span><span class="p">);</span>
</span><span class='line'>    <span class="nv">$re</span> <span class="o">=</span> <span class="nb">curl_exec</span><span class="p">(</span><span class="nv">$curl</span><span class="p">);</span> <span class="c1">//执行</span>
</span><span class='line'>    <span class="k">if</span> <span class="p">(</span><span class="nb">curl_errno</span><span class="p">(</span><span class="nv">$curl</span><span class="p">))</span> <span class="p">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="k">NULL</span><span class="p">;</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>    <span class="k">return</span> <span class="nv">$re</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>相信大家都很明白了吧，设置CURLOPT_REFERER这个属性是最关键的一步。</p>

<h2>转发图片</h2>

<p>一般情况下，我们在PHP中<code>echo 'hello'</code>是将字符串作为纯文本输出到浏览器中的。至于为什么是纯文本，这就又要扯到Response Header中的Content-Type了，这便是用来指定内容类型的。这个Content-Type实际上是MIME（Multipurpose Internet Mail Extensions，多用途互联网邮件扩展）标准中的一部分，是通过好几个RFC定义的。大多数网页都是“<code>text/html</code>”。如果要用来显示图片，就需要修改这个字段，对应不同的图片格式，有<code>image/jpeg</code>、<code>image/png</code>、<code>image/gif</code>等等。</p>

<p>为了在PHP中使用echo命令输出图片信息，我们就需要修改header信息。根据源图片URL中的后缀名，我们可以相应地使用诸如<code>header("Content-Type: image/jpeg");</code>来修改header信息。接下来，<code>echo fetch_bbs_image($url);</code>即可。</p>

<h2>本地缓存</h2>

<p>如果每次访问图片我们都需要在服务器上使用curl远程下载一个下来，是比较消耗资源的，我们可以做一个简单的本地缓存，第一次调用时进行下载的操作，今后就可以直接从本地缓存调取图片了。这时候我们需要保证下载回来的图片的文件名都是唯一的。这个好办，通过分析小百合BBS的文件路径，我们可以发现文件路径都是类似<code>http://bbs.nju.edu.cn/file/xxx/xxxx.jpg</code>的。所以我们只要把<code>xxx/xxxx.jpg</code>保存作为文件名即可，当然需要把其中的斜杠替换成其他字符。</p>

<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class='php'><span class='line'><span class="nb">define</span><span class="p">(</span><span class="nx">CACHE_DIR</span><span class="p">,</span> <span class="s1">&#39;./lily_images/&#39;</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="k">function</span> <span class="nf">get_filename</span><span class="p">(</span><span class="nv">$url</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">return</span> <span class="nx">CACHE_DIR</span> <span class="o">.</span> <span class="nb">str_replace</span><span class="p">(</span><span class="s1">&#39;/&#39;</span><span class="p">,</span> <span class="s1">&#39;-&#39;</span><span class="p">,</span> <span class="nx">substr</span><span class="p">(</span><span class="nv">$url</span><span class="p">,</span> <span class="mi">27</span><span class="p">));</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="k">if</span> <span class="p">(</span><span class="nb">file_exists</span><span class="p">(</span><span class="nx">get_filename</span><span class="p">(</span><span class="nv">$url</span><span class="p">)))</span> <span class="p">{</span> <span class="c1">// cache hit!</span>
</span><span class='line'>    <span class="k">echo</span> <span class="nb">file_get_contents</span><span class="p">(</span><span class="nx">get_filename</span><span class="p">(</span><span class="nv">$url</span><span class="p">));</span>
</span><span class='line'>    <span class="k">exit</span><span class="p">();</span>
</span><span class='line'><span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="c1">// save cache</span>
</span><span class='line'>    <span class="nv">$filename</span> <span class="o">=</span> <span class="nx">get_filename</span><span class="p">(</span><span class="nv">$url</span><span class="p">);</span>
</span><span class='line'>    <span class="nb">file_put_contents</span><span class="p">(</span> <span class="nv">$filename</span><span class="p">,</span> <span class="nx">fetch_bbs_image</span><span class="p">(</span><span class="nv">$url</span><span class="p">)</span> <span class="p">);</span>
</span><span class='line'>    <span class="k">echo</span> <span class="nb">file_get_contents</span><span class="p">(</span><span class="nv">$filename</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<h2>图片二次处理</h2>

<p>实际应用时，我们发现有时候小百合BBS中的图片都是几百万像素的照片原图，在校园网内访问这些图片自然是毫无压力的，而且Web版BBS中有JavaScript来自动将过大的图片强制缩小显示，以免撑破版面。但是到了外站，如此大的图片就显得有些夸张了，利用PHP中的GD图形库，我们可以方便地进行图片的二次处理，首要的需求自然是将过大的图片缩小。GD库并没有直接按比例缩小图片的功能（如果有也太高级了），好在网上早已有许多现成的代码片段，我们便无需再次发明轮子了。参考了<a href="http://mediumexposure.com/smart-image-resizing-while-preserving-transparency-php-and-gd-library/" title="Smart Image Resizing while Preserving Transparency With PHP and GD Library">Maxim Chernyak</a>的代码片段，我们可以很轻松地实现这一功能。需要说明的是，原始的代码片段中对于小图片也会进行放大处理，而且它对GIF动画的处理会让它变成静止图片，因此需要对其进行小小的修改来满足我们的需要。</p>

<figure class='code'><figcaption><span>修改后的主脚本和图片缩小函数  </span></figcaption>
 <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
<span class='line-number'>65</span>
<span class='line-number'>66</span>
<span class='line-number'>67</span>
<span class='line-number'>68</span>
<span class='line-number'>69</span>
<span class='line-number'>70</span>
<span class='line-number'>71</span>
<span class='line-number'>72</span>
<span class='line-number'>73</span>
<span class='line-number'>74</span>
<span class='line-number'>75</span>
<span class='line-number'>76</span>
<span class='line-number'>77</span>
<span class='line-number'>78</span>
<span class='line-number'>79</span>
<span class='line-number'>80</span>
<span class='line-number'>81</span>
<span class='line-number'>82</span>
<span class='line-number'>83</span>
<span class='line-number'>84</span>
<span class='line-number'>85</span>
<span class='line-number'>86</span>
<span class='line-number'>87</span>
<span class='line-number'>88</span>
<span class='line-number'>89</span>
<span class='line-number'>90</span>
<span class='line-number'>91</span>
<span class='line-number'>92</span>
<span class='line-number'>93</span>
<span class='line-number'>94</span>
<span class='line-number'>95</span>
<span class='line-number'>96</span>
<span class='line-number'>97</span>
<span class='line-number'>98</span>
<span class='line-number'>99</span>
<span class='line-number'>100</span>
<span class='line-number'>101</span>
<span class='line-number'>102</span>
<span class='line-number'>103</span>
<span class='line-number'>104</span>
<span class='line-number'>105</span>
<span class='line-number'>106</span>
<span class='line-number'>107</span>
<span class='line-number'>108</span>
<span class='line-number'>109</span>
<span class='line-number'>110</span>
<span class='line-number'>111</span>
<span class='line-number'>112</span>
<span class='line-number'>113</span>
<span class='line-number'>114</span>
<span class='line-number'>115</span>
<span class='line-number'>116</span>
<span class='line-number'>117</span>
<span class='line-number'>118</span>
<span class='line-number'>119</span>
<span class='line-number'>120</span>
<span class='line-number'>121</span>
<span class='line-number'>122</span>
<span class='line-number'>123</span>
<span class='line-number'>124</span>
<span class='line-number'>125</span>
<span class='line-number'>126</span>
<span class='line-number'>127</span>
<span class='line-number'>128</span>
<span class='line-number'>129</span>
<span class='line-number'>130</span>
<span class='line-number'>131</span>
<span class='line-number'>132</span>
<span class='line-number'>133</span>
<span class='line-number'>134</span>
<span class='line-number'>135</span>
<span class='line-number'>136</span>
<span class='line-number'>137</span>
<span class='line-number'>138</span>
<span class='line-number'>139</span>
<span class='line-number'>140</span>
<span class='line-number'>141</span>
<span class='line-number'>142</span>
<span class='line-number'>143</span>
<span class='line-number'>144</span>
<span class='line-number'>145</span>
<span class='line-number'>146</span>
<span class='line-number'>147</span>
<span class='line-number'>148</span>
<span class='line-number'>149</span>
<span class='line-number'>150</span>
<span class='line-number'>151</span>
<span class='line-number'>152</span>
<span class='line-number'>153</span>
<span class='line-number'>154</span>
<span class='line-number'>155</span>
<span class='line-number'>156</span>
<span class='line-number'>157</span>
<span class='line-number'>158</span>
<span class='line-number'>159</span>
<span class='line-number'>160</span>
<span class='line-number'>161</span>
</pre></td><td class='code'><pre><code class='php'><span class='line'><span class="k">switch</span> <span class="p">(</span><span class="nx">strtolower</span><span class="p">(</span><span class="nx">substr</span><span class="p">(</span><span class="nv">$url</span><span class="p">,</span> <span class="o">-</span> <span class="mi">3</span><span class="p">)))</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">case</span> <span class="s1">&#39;jpg&#39;</span> <span class="o">:</span>
</span><span class='line'>    <span class="k">case</span> <span class="s1">&#39;pge&#39;</span> <span class="o">:</span>
</span><span class='line'>        <span class="nv">$type</span> <span class="o">=</span> <span class="s1">&#39;image/jpeg&#39;</span><span class="p">;</span>
</span><span class='line'>        <span class="k">break</span><span class="p">;</span>
</span><span class='line'>    <span class="k">case</span> <span class="s1">&#39;png&#39;</span> <span class="o">:</span>
</span><span class='line'>        <span class="nv">$type</span> <span class="o">=</span> <span class="s1">&#39;image/png&#39;</span><span class="p">;</span>
</span><span class='line'>        <span class="k">break</span><span class="p">;</span>
</span><span class='line'>    <span class="k">case</span> <span class="s1">&#39;gif&#39;</span> <span class="o">:</span>
</span><span class='line'>        <span class="nv">$type</span> <span class="o">=</span> <span class="s1">&#39;image/gif&#39;</span><span class="p">;</span>
</span><span class='line'>        <span class="k">break</span><span class="p">;</span>
</span><span class='line'>    <span class="k">default</span> <span class="o">:</span>
</span><span class='line'>        <span class="nv">$type</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'><span class="nx">header</span><span class="p">(</span><span class="s2">&quot;Content-Type: </span><span class="si">$type</span><span class="s2">&quot;</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="k">if</span> <span class="p">(</span><span class="nb">file_exists</span><span class="p">(</span><span class="nx">get_filename</span><span class="p">(</span><span class="nv">$url</span><span class="p">)))</span> <span class="p">{</span> <span class="c1">// cache hit!</span>
</span><span class='line'>    <span class="k">echo</span> <span class="nb">file_get_contents</span><span class="p">(</span><span class="nx">get_filename</span><span class="p">(</span><span class="nv">$url</span><span class="p">));</span>
</span><span class='line'>    <span class="k">exit</span><span class="p">();</span>
</span><span class='line'><span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="c1">// resize it and save cache</span>
</span><span class='line'>    <span class="nv">$filename</span> <span class="o">=</span> <span class="nx">get_filename</span><span class="p">(</span><span class="nv">$url</span><span class="p">);</span>
</span><span class='line'>    <span class="nv">$img_content</span> <span class="o">=</span> <span class="nx">fetch_bbs_image</span><span class="p">(</span><span class="nv">$url</span><span class="p">);</span>
</span><span class='line'>    <span class="nb">file_put_contents</span><span class="p">(</span><span class="nv">$filename</span><span class="p">,</span> <span class="nv">$img_content</span><span class="p">);</span>
</span><span class='line'>    <span class="k">if</span> <span class="p">(</span><span class="nv">$type</span> <span class="o">==</span> <span class="s1">&#39;image/png&#39;</span> <span class="o">||</span> <span class="nv">$type</span> <span class="o">==</span> <span class="s1">&#39;image/jpeg&#39;</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="nx">smart_resize_image</span><span class="p">(</span><span class="nv">$filename</span><span class="p">,</span> <span class="mi">550</span><span class="p">,</span> <span class="mi">550</span><span class="p">,</span> <span class="k">true</span><span class="p">);</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>    <span class="k">echo</span> <span class="nb">file_get_contents</span><span class="p">(</span><span class="nv">$filename</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="sd">/**</span>
</span><span class='line'><span class="sd"> * Smart Image Resizing while Preserving Transparency With PHP and GD Library</span>
</span><span class='line'><span class="sd"> * tinily modified by @author clippit</span>
</span><span class='line'><span class="sd"> *</span>
</span><span class='line'><span class="sd"> * @author Maxim Chernyak</span>
</span><span class='line'><span class="sd"> * @link http://mediumexposure.com/smart-image-resizing-while-preserving-transparency-php-and-gd-library/</span>
</span><span class='line'><span class="sd"> */</span>
</span><span class='line'><span class="k">function</span> <span class="nf">smart_resize_image</span><span class="p">(</span><span class="nv">$file</span><span class="p">,</span> <span class="nv">$width</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">$height</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">$proportional</span> <span class="o">=</span> <span class="k">false</span><span class="p">,</span> <span class="nv">$output</span> <span class="o">=</span> <span class="s1">&#39;file&#39;</span><span class="p">,</span> <span class="nv">$delete_original</span> <span class="o">=</span> <span class="k">true</span><span class="p">,</span> <span class="nv">$use_linux_commands</span> <span class="o">=</span> <span class="k">false</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">if</span> <span class="p">(</span><span class="nv">$height</span> <span class="o">&lt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="nv">$width</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="k">false</span><span class="p">;</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nv">$info</span> <span class="o">=</span> <span class="nb">getimagesize</span><span class="p">(</span><span class="nv">$file</span><span class="p">);</span>
</span><span class='line'>    <span class="nv">$image</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">if</span> <span class="p">(</span><span class="nv">$info</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="nv">$width</span> <span class="o">||</span> <span class="nv">$info</span> <span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="nv">$height</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="c1">// if the original image is too small to the target width and height, then do not zoom in</span>
</span><span class='line'>        <span class="k">return</span> <span class="k">false</span><span class="p">;</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nv">$final_width</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span><span class='line'>    <span class="nv">$final_height</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span><span class='line'>    <span class="k">list</span> <span class="p">(</span> <span class="nv">$width_old</span><span class="p">,</span> <span class="nv">$height_old</span> <span class="p">)</span> <span class="o">=</span> <span class="nv">$info</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">if</span> <span class="p">(</span><span class="nv">$proportional</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="k">if</span> <span class="p">(</span><span class="nv">$width</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
</span><span class='line'>            <span class="nv">$factor</span> <span class="o">=</span> <span class="nv">$height</span> <span class="o">/</span> <span class="nv">$height_old</span><span class="p">;</span>
</span><span class='line'>        <span class="k">elseif</span> <span class="p">(</span><span class="nv">$height</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
</span><span class='line'>            <span class="nv">$factor</span> <span class="o">=</span> <span class="nv">$width</span> <span class="o">/</span> <span class="nv">$width_old</span><span class="p">;</span>
</span><span class='line'>        <span class="k">else</span>
</span><span class='line'>            <span class="nv">$factor</span> <span class="o">=</span> <span class="nx">min</span><span class="p">(</span><span class="nv">$width</span> <span class="o">/</span> <span class="nv">$width_old</span><span class="p">,</span> <span class="nv">$height</span> <span class="o">/</span> <span class="nv">$height_old</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'>        <span class="nv">$final_width</span> <span class="o">=</span> <span class="nx">round</span><span class="p">(</span><span class="nv">$width_old</span> <span class="o">*</span> <span class="nv">$factor</span><span class="p">);</span>
</span><span class='line'>        <span class="nv">$final_height</span> <span class="o">=</span> <span class="nx">round</span><span class="p">(</span><span class="nv">$height_old</span> <span class="o">*</span> <span class="nv">$factor</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'>    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span><span class='line'>        <span class="nv">$final_width</span> <span class="o">=</span> <span class="p">(</span><span class="nv">$width</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="o">?</span> <span class="nv">$width_old</span> <span class="o">:</span> <span class="nv">$width</span><span class="p">;</span>
</span><span class='line'>        <span class="nv">$final_height</span> <span class="o">=</span> <span class="p">(</span><span class="nv">$height</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="o">?</span> <span class="nv">$height_old</span> <span class="o">:</span> <span class="nv">$height</span><span class="p">;</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">switch</span> <span class="p">(</span><span class="nv">$info</span> <span class="p">[</span><span class="mi">2</span><span class="p">])</span> <span class="p">{</span>
</span><span class='line'>        <span class="k">case</span> <span class="nx">IMAGETYPE_GIF</span> <span class="o">:</span>
</span><span class='line'>            <span class="nv">$image</span> <span class="o">=</span> <span class="nx">imagecreatefromgif</span><span class="p">(</span><span class="nv">$file</span><span class="p">);</span>
</span><span class='line'>            <span class="k">break</span><span class="p">;</span>
</span><span class='line'>        <span class="k">case</span> <span class="nx">IMAGETYPE_JPEG</span> <span class="o">:</span>
</span><span class='line'>            <span class="nv">$image</span> <span class="o">=</span> <span class="nx">imagecreatefromjpeg</span><span class="p">(</span><span class="nv">$file</span><span class="p">);</span>
</span><span class='line'>            <span class="k">break</span><span class="p">;</span>
</span><span class='line'>        <span class="k">case</span> <span class="nx">IMAGETYPE_PNG</span> <span class="o">:</span>
</span><span class='line'>            <span class="nv">$image</span> <span class="o">=</span> <span class="nx">imagecreatefrompng</span><span class="p">(</span><span class="nv">$file</span><span class="p">);</span>
</span><span class='line'>            <span class="k">break</span><span class="p">;</span>
</span><span class='line'>        <span class="k">default</span> <span class="o">:</span>
</span><span class='line'>            <span class="k">return</span> <span class="k">false</span><span class="p">;</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nv">$image_resized</span> <span class="o">=</span> <span class="nx">imagecreatetruecolor</span><span class="p">(</span><span class="nv">$final_width</span><span class="p">,</span> <span class="nv">$final_height</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">if</span> <span class="p">((</span><span class="nv">$info</span> <span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="nx">IMAGETYPE_GIF</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="nv">$info</span> <span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="nx">IMAGETYPE_PNG</span><span class="p">))</span> <span class="p">{</span>
</span><span class='line'>        <span class="nv">$trnprt_indx</span> <span class="o">=</span> <span class="nx">imagecolortransparent</span><span class="p">(</span><span class="nv">$image</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'>        <span class="c1">// If we have a specific transparent color</span>
</span><span class='line'>        <span class="k">if</span> <span class="p">(</span><span class="nv">$trnprt_indx</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'>            <span class="c1">// Get the original image&#39;s transparent color&#39;s RGB values</span>
</span><span class='line'>            <span class="nv">$trnprt_color</span> <span class="o">=</span> <span class="nx">imagecolorsforindex</span><span class="p">(</span><span class="nv">$image</span><span class="p">,</span> <span class="nv">$trnprt_indx</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'>            <span class="c1">// Allocate the same color in the new image resource</span>
</span><span class='line'>            <span class="nv">$trnprt_indx</span> <span class="o">=</span> <span class="nx">imagecolorallocate</span><span class="p">(</span><span class="nv">$image_resized</span><span class="p">,</span> <span class="nv">$trnprt_color</span> <span class="p">[</span><span class="s1">&#39;red&#39;</span><span class="p">],</span> <span class="nv">$trnprt_color</span> <span class="p">[</span><span class="s1">&#39;green&#39;</span><span class="p">],</span> <span class="nv">$trnprt_color</span> <span class="p">[</span><span class="s1">&#39;blue&#39;</span><span class="p">]);</span>
</span><span class='line'>
</span><span class='line'>            <span class="c1">// Completely fill the background of the new image with allocated color.</span>
</span><span class='line'>            <span class="nx">imagefill</span><span class="p">(</span><span class="nv">$image_resized</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">$trnprt_indx</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'>            <span class="c1">// Set the background color for new image to transparent</span>
</span><span class='line'>            <span class="nx">imagecolortransparent</span><span class="p">(</span><span class="nv">$image_resized</span><span class="p">,</span> <span class="nv">$trnprt_indx</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'>        <span class="p">}</span> <span class="c1">// Always make a transparent background color for PNGs that don&#39;t have one allocated already</span>
</span><span class='line'><span class="k">elseif</span> <span class="p">(</span><span class="nv">$info</span> <span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="nx">IMAGETYPE_PNG</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'>            <span class="c1">// Turn off transparency blending (temporarily)</span>
</span><span class='line'>            <span class="nx">imagealphablending</span><span class="p">(</span><span class="nv">$image_resized</span><span class="p">,</span> <span class="k">false</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'>            <span class="c1">// Create a new transparent color for image</span>
</span><span class='line'>            <span class="nv">$color</span> <span class="o">=</span> <span class="nx">imagecolorallocatealpha</span><span class="p">(</span><span class="nv">$image_resized</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">127</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'>            <span class="c1">// Completely fill the background of the new image with allocated color.</span>
</span><span class='line'>            <span class="nx">imagefill</span><span class="p">(</span><span class="nv">$image_resized</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">$color</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'>            <span class="c1">// Restore transparency blending</span>
</span><span class='line'>            <span class="nx">imagesavealpha</span><span class="p">(</span><span class="nv">$image_resized</span><span class="p">,</span> <span class="k">true</span><span class="p">);</span>
</span><span class='line'>        <span class="p">}</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nx">imagecopyresampled</span><span class="p">(</span><span class="nv">$image_resized</span><span class="p">,</span> <span class="nv">$image</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">$final_width</span><span class="p">,</span> <span class="nv">$final_height</span><span class="p">,</span> <span class="nv">$width_old</span><span class="p">,</span> <span class="nv">$height_old</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">if</span> <span class="p">(</span><span class="nv">$delete_original</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="k">if</span> <span class="p">(</span><span class="nv">$use_linux_commands</span><span class="p">)</span>
</span><span class='line'>            <span class="nb">exec</span><span class="p">(</span><span class="s1">&#39;rm &#39;</span> <span class="o">.</span> <span class="nv">$file</span><span class="p">);</span>
</span><span class='line'>        <span class="k">else</span>
</span><span class='line'>            <span class="o">@</span><span class="nb">unlink</span><span class="p">(</span><span class="nv">$file</span><span class="p">);</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">switch</span> <span class="p">(</span><span class="nx">strtolower</span><span class="p">(</span><span class="nv">$output</span><span class="p">))</span> <span class="p">{</span>
</span><span class='line'>        <span class="k">case</span> <span class="s1">&#39;browser&#39;</span> <span class="o">:</span>
</span><span class='line'>            <span class="nv">$mime</span> <span class="o">=</span> <span class="nb">image_type_to_mime_type</span><span class="p">(</span><span class="nv">$info</span> <span class="p">[</span><span class="mi">2</span><span class="p">]);</span>
</span><span class='line'>            <span class="nx">header</span><span class="p">(</span><span class="s2">&quot;Content-type: </span><span class="si">$mime</span><span class="s2">&quot;</span><span class="p">);</span>
</span><span class='line'>            <span class="nv">$output</span> <span class="o">=</span> <span class="k">NULL</span><span class="p">;</span>
</span><span class='line'>            <span class="k">break</span><span class="p">;</span>
</span><span class='line'>        <span class="k">case</span> <span class="s1">&#39;file&#39;</span> <span class="o">:</span>
</span><span class='line'>            <span class="nv">$output</span> <span class="o">=</span> <span class="nv">$file</span><span class="p">;</span>
</span><span class='line'>            <span class="k">break</span><span class="p">;</span>
</span><span class='line'>        <span class="k">case</span> <span class="s1">&#39;return&#39;</span> <span class="o">:</span>
</span><span class='line'>            <span class="k">return</span> <span class="nv">$image_resized</span><span class="p">;</span>
</span><span class='line'>            <span class="k">break</span><span class="p">;</span>
</span><span class='line'>        <span class="k">default</span> <span class="o">:</span>
</span><span class='line'>            <span class="k">break</span><span class="p">;</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">switch</span> <span class="p">(</span><span class="nv">$info</span> <span class="p">[</span><span class="mi">2</span><span class="p">])</span> <span class="p">{</span>
</span><span class='line'>        <span class="k">case</span> <span class="nx">IMAGETYPE_GIF</span> <span class="o">:</span>
</span><span class='line'>            <span class="nx">imagegif</span><span class="p">(</span><span class="nv">$image_resized</span><span class="p">,</span> <span class="nv">$output</span><span class="p">);</span>
</span><span class='line'>            <span class="k">break</span><span class="p">;</span>
</span><span class='line'>        <span class="k">case</span> <span class="nx">IMAGETYPE_JPEG</span> <span class="o">:</span>
</span><span class='line'>            <span class="nx">imagejpeg</span><span class="p">(</span><span class="nv">$image_resized</span><span class="p">,</span> <span class="nv">$output</span><span class="p">);</span>
</span><span class='line'>            <span class="k">break</span><span class="p">;</span>
</span><span class='line'>        <span class="k">case</span> <span class="nx">IMAGETYPE_PNG</span> <span class="o">:</span>
</span><span class='line'>            <span class="nx">imagepng</span><span class="p">(</span><span class="nv">$image_resized</span><span class="p">,</span> <span class="nv">$output</span><span class="p">);</span>
</span><span class='line'>            <span class="k">break</span><span class="p">;</span>
</span><span class='line'>        <span class="k">default</span> <span class="o">:</span>
</span><span class='line'>            <span class="k">return</span> <span class="k">false</span><span class="p">;</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">return</span> <span class="k">true</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<h2>调用该PHP脚本</h2>

<p>我们将这个脚本放在可以访问到的Web目录中，并且建立一个<code>CACHE_DIR</code>中指定的目录，给它赋予775权限。URL参数我们通过GET参数来获得。为了防止一些莫名其妙的编码问题，并且掩耳盗铃一下，这个参数我们采用Base64编码后再进行URL转义。</p>

<p>同时，如果传入的URL参数不是来自<code>http://bbs.nju.edu.cn</code>的，就直接用<code>header("Location: $url")</code>重定向到目标网址，不处理该图片文件。</p>

<h2>修改图片引用地址</h2>

<p>最开始我们说到，图片都是<code>&lt;img src="http://bbs.nju.edu.cn/XXXXXXXXXXXX" alt="" /&gt;</code>这样的，在发布的时候就需要把其中的src全部改掉了。使用强大的正则表达式，我们可以轻松地在大量HTML中替换这些，以Python脚本为例，我们只需两个函数：</p>

<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="kn">import</span> <span class="nn">base64</span><span class="o">,</span> <span class="nn">re</span><span class="o">,</span> <span class="nn">urllib</span>
</span><span class='line'>
</span><span class='line'><span class="k">def</span> <span class="nf">encode_url</span><span class="p">(</span><span class="n">match</span><span class="p">):</span>
</span><span class='line'>    <span class="n">url</span> <span class="o">=</span> <span class="n">urllib</span><span class="o">.</span><span class="n">pathname2url</span><span class="p">(</span> <span class="n">base64</span><span class="o">.</span><span class="n">b64encode</span><span class="p">(</span><span class="n">match</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span> <span class="p">)</span>
</span><span class='line'>    <span class="k">return</span> <span class="s">&#39;&#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span> <span class="p">(</span><span class="s">&#39;&lt;img alt=&quot;&quot; src=&quot;&#39;</span><span class="p">,</span> <span class="n">GET_IMAGE</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="s">&#39;&quot;&#39;</span><span class="p">)</span> <span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="k">def</span> <span class="nf">image_proxy</span><span class="p">(</span><span class="n">text</span><span class="p">):</span>
</span><span class='line'>    <span class="k">return</span> <span class="n">re</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="s">r&#39;&lt;img alt=&quot;&quot; src=&quot;([^&quot;]+)&quot;&#39;</span><span class="p">,</span> <span class="n">encode_url</span><span class="p">,</span> <span class="n">text</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>在需要的时候，将HTML代码字符串传入<code>image_proxy</code>即可。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[360 vs. QQ：出来混，迟早是要还的]]></title>
    <link href="http://blog.dayanjia.com/2010/11/360-vs-qq-fight-on-battlefield-die-on-battlefield/"/>
    <updated>2010-11-05T00:00:00+08:00</updated>
    <id>http://blog.dayanjia.com/2010/11/360-vs-qq-fight-on-battlefield-die-on-battlefield</id>
    <content type="html"><![CDATA[<p>中国的互联网迈入了乱象丛生的战国时代，最近奇虎360和腾讯QQ的大规模直接冲撞便是一出精彩纷呈的年度大戏。从年初QQ强推QQ医生，到江湖风传QQ扫描硬盘，到360推出隐私保护器，到弹窗大战，到扣扣保镖，到一山容不得二虎……情节跌宕起伏，扣人心弦。腾讯在这次事件中元气大伤，被广大人民群众的口水淹没；360不断装纯装嫩博取了许多人的同情和支持。有言道：“出来混，迟早是要还的”。那么，常在江湖飘的360和QQ，作为中国装机量最大的两个软件，如何才能真正对自己的用户负责？</p>

<p><!--more-->
<h2>八爪鱼腾讯的报应</h2>
<a rel="attachment wp-att-1102" href="http://dayanjia.com/2010/11/360-vs-qq-fight-on-battlefield-die-on-battlefield.html/360vsqq-13"><img class="alignnone size-large wp-image-1102" title="360vsQQ (13)" src="http://dayanjia.com/wp-content/uploads/2010/11/360vsQQ-13-580x435.jpg" alt="" width="580" height="435" /></a></p>

<p>腾讯有如八爪鱼一般，利用QQ聊天工具的用户黏度，将触手伸向了互联网的每一个角落。腾讯所过之处，无不掀起一场恐怖风暴，人们纷纷大喊：“腾讯来了！”早期的腾讯缓慢扩张，涉足的往往是较为成熟的领域；随着如今互联网的发展，腾讯越来越快地跟进到新兴领域。面对这样一个巨无霸的山寨主，小公司毫无招架之力，于是骂道“狗日”的腾讯（《计算机世界》某期封面文章）。</p>

<p><a rel="attachment wp-att-1105" href="http://dayanjia.com/2010/11/360-vs-qq-fight-on-battlefield-die-on-battlefield.html/360vsqq-14"><img class="size-medium wp-image-1105 alignleft" title="360vsQQ (14)" src="http://dayanjia.com/wp-content/uploads/2010/11/360vsQQ-14-300x225.jpg" alt="" width="300" height="225" /></a>作为一个互联网巨头，不应一味模仿理念做自己的产品，而是应当采取收购的做法。腾讯有义务做道德上的表率，将整个行业领向正确的发展道路，但是他没有。于是大家对腾讯既敬畏，又恨得咬牙切齿。</p>

<p>于是人们长期积累的怨恨在360的导火索下爆发了！360一开始抓住的QQ小辫子是“偷窥用户隐私”，但是实际上人们口诛笔伐的并不是这一点，而是前面说到的所谓“抄袭”。
<blockquote><strong>这便是腾讯的报应，是腾讯的活该。</strong></blockquote>
<strong>就算没有360，腾讯的这一发展模式迟早也会带来这一场气势浩大的声讨。这场“人民战争”，是腾讯迟早要必须面对的，只不过它比想象中来得早了一些。</strong>
<h2>360：不是不报，时候未到</h2>
<a rel="attachment wp-att-1106" href="http://dayanjia.com/2010/11/360-vs-qq-fight-on-battlefield-die-on-battlefield.html/360vsqq-18"><img class="size-medium wp-image-1106 alignright" title="360vsQQ (18)" src="http://dayanjia.com/wp-content/uploads/2010/11/360vsQQ-18-300x225.jpg" alt="" width="300" height="225" /></a>
我曾经说过，<strong>360是邪教</strong>。它利用各种手段愚弄、恐吓用户，让用户对360的敬仰之情上升到滔滔江水的高度，让用户放弃自己判断的能力，对360五体投地地信任。</p>

<p>360的惯用手法有：
<ul>
	<li>不断宣称只有自己的东西才是安全的，在用户面前扮演救世主的角色（例如<a title="360欺骗4亿网民 胡乱解读“超级工厂”病毒 ——卡巴斯基关于360胡乱解读“超级工厂”的声明" href="http://www.kaspersky.com.cn/KL-AboutUs/news2010/10n/101013.htm" target="_blank">卡巴斯基对超级工厂的解读</a>和<a title="360回应：卡巴斯基对“超级工厂”的反应迟到了3个月" href="http://bbs.360.cn/3229787/39398799.html" target="_blank">360的反驳</a>）</li>
	<li><strong>通过“体检”“开始时间”等功能，量化这个“安全”的概念，让使用者深陷数字带来的心理安慰，还在用户群中形成相互攀比的风气</strong></li>
	<li>动用网络水军，给自己制造舆论优势氛围（有传言说360给自己的<a title="傅盛微博公布360卸载对手录像 称360忘性大" href="http://www.cnbeta.com/articles/126199.htm" target="_blank">水军工资由5毛涨到7毛</a>）</li>
</ul>
<a rel="attachment wp-att-1107" href="http://dayanjia.com/2010/11/360-vs-qq-fight-on-battlefield-die-on-battlefield.html/360vsqq-19"><img class="alignnone size-large wp-image-1107" title="360vsQQ (19)" src="http://dayanjia.com/wp-content/uploads/2010/11/360vsQQ-19-580x435.jpg" alt="" width="580" height="435" /></a>
<blockquote>360的邪恶本质早在它和几大安全厂商打口水战时就开始显露了，但是广大善良的网民还沉浸在“删流氓软件”“打补丁”等美好世界的幻境中。</blockquote>
<h2>就事论事</h2>
这次事件仍然在发展中，今天上午的最新消息是腾讯纠集的五厂商联合发布“不兼容360”的宣言，还历数了<a title="五厂商历数360八谎言：名为云安全实为云暗杀" href="http://www.cnbeta.com/articles/126264.htm" target="_blank">360的八大谎言</a>。事实上，360和QQ大战的全面升级是以360发布扣扣保镖为里程碑的。这个扣扣保镖动用了<a title="瑞星第三方研究报告：360扣扣保镖为何激怒腾讯" href="http://www.cnbeta.com/articles/126254.htm" target="_blank">注入进程</a>等非常恶劣的手段，不过这可谓是360的惯用招数。</p>

<p>如果我们就事论事，不难发现，无论是腾讯的抄袭还是周鸿祎的3721背景其实都和这件事没什么直接的联系。但是清楚的是，<strong>360一直在挑战软件工程的职业道德底线</strong>。腾讯的“二选一”手段固然不道德，劫持了用户的选择权，但是360的行为激怒的是整个行业。商业公司自然以资本利益为首位，用户利益为第二，这点毋庸置疑，但是商业公司必须遵循游戏规则。这种公然挑战行业的行为必然会遭到许多人的不齿，360这样混，迟早是要还的。<strong>用一句文革时期的话说：</strong>
<blockquote><strong>360必将永远地钉在软件工程行业的耻辱柱上！</strong></blockquote>
<h2>游戏规则何在？</h2>
让我们再回到用户隐私的问题上来吧，显然窃取隐私也是一件有违职业道德的事情。但是在中国的互联网企业，你敢不配合国家安全部门进行信息的监控、审查吗？人在屋檐下，不得不低头，作为景德镇的村民，你我都深知所谓“用户隐私”只是一个笑话而已，只能理解万岁了。</p>

<p>大家心里都明白，行业内的竞争这样子搞肯定是不行的。“职业道德”在镇上又只是一朵浮云，那我们应该靠什么来约束呢？面对那些强行捆绑安装，恶意禁止使用竞争对手产品，大肆宣布“我和他不兼容，你们自己看着办”……种种乱象时，我们不禁要问：游戏规则何在？</p>

<p>法律的缺失，即使有了法律也不能保障执行，在纷纷搞关系化的人治的镇上，契约化的法治恐怕难有用武之地，那么这个行业就会永远地乱下去。难道我们只能归咎于上梁不正下梁歪吗？谁知道呢，反正出来混，迟早要还的，人们才不管你是上梁还是下梁。</p>

<p><a rel="attachment wp-att-1098" href="http://dayanjia.com/2010/11/360-vs-qq-fight-on-battlefield-die-on-battlefield.html/360vsqq"><img class="alignnone size-large wp-image-1098" title="360vsQQ" src="http://dayanjia.com/wp-content/uploads/2010/11/360vsQQ-580x348.jpg" alt="" width="580" height="348" /></a></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[一段能瞬间秒杀所有版本Internet Explorer的简单HTML代码]]></title>
    <link href="http://blog.dayanjia.com/2010/10/how-to-crash-any-version-of-internet-explorer-with-simple-html/"/>
    <updated>2010-10-29T00:00:00+08:00</updated>
    <id>http://blog.dayanjia.com/2010/10/how-to-crash-any-version-of-internet-explorer-with-simple-html</id>
    <content type="html"><![CDATA[<p>许多人都<a title="why so many geeks hate Internet Explorer" href="http://www.howtogeek.com/howto/32372/htg-explains-why-do-so-many-geeks-hate-internet-explorer/" target="_blank">非常讨厌Internet Explorer</a>，在西方万圣节即将到来之际，让我们来看一个真正吓人的东西——如何用一段简单的HTML和CSS，将任何版本的IE搞死。</p>

<p>我们只需要简单地打开任意文本编辑器，将下面的代码复制进去，然后保存文件，例如<code>SomeFilename.html</code>。</p>

<p><!--more--></p>

<p>[html]<br />
&lt;html&gt;&lt;head&gt;<br />
&lt;style type=&quot;text/css&quot;&gt;<br />
#a {<br />
        margin:0 10px 10px;<br />
}</p>

<p>#b {<br />
        width:100%;<br />
}</p>

<p>&lt;/style&gt;<br />
&lt;title&gt;IE Crasher&lt;/title&gt;<br />
&lt;/head&gt;<br />
&lt;body&gt;<br />
&lt;table&gt;&lt;tr&gt;&lt;td&gt;<br />
&lt;div id=&quot;a&quot;&gt;<br />
&lt;form id=&quot;b&quot;&gt;<br />
&lt;input type=&quot;text&quot; name=&quot;test&quot;/&gt;<br />
&lt;/div&gt;<br />
&lt;/td&gt;&lt;td width=&quot;1&quot;&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;<br />
&lt;/body&gt;&lt;/html&gt;<br />
[/html]</p>

<p><a rel="attachment wp-att-1085" href="http://dayanjia.com/2010/10/how-to-crash-any-version-of-internet-explorer-with-simple-html.html/ie9-crash"><img class="alignleft size-thumbnail wp-image-1085" title="IE9 crash" src="http://dayanjia.com/wp-content/uploads/2010/10/IE9-crash-200x113.png" alt="" width="200" height="113" /></a>然后用Internet Explorer打开它——哈哈！它很惨地死给你看了。</p>

<p>这个bug几乎存在于现在流行的所有IE版本，从IE6到刚刚发布的Internet Explorer 9的第6个平台预览版。在IE6中，该页面往往显示为空白，但是整个窗口都无法交互。在IE8中，崩溃来得很彻底。而IE9 Beta增强了坚固性，经过我的实测，这个页面又是会导致整个进程无响应，而有时IE9会提示你网页存在的问题，但是这都无法避免崩溃的现实。</p>

<p><a rel="attachment wp-att-1085" href="http://dayanjia.com/2010/10/how-to-crash-any-version-of-internet-explorer-with-simple-html.html/ie9-crash"><img class="alignnone size-large wp-image-1085" title="IE9 crash" src="http://dayanjia.com/wp-content/uploads/2010/10/IE9-crash-580x328.png" alt="" width="580" height="328" /></a></p>

<p>事实上，<a title="Pure HTML &amp; CSS code that crashes IE7/8" href="http://groups.google.com/group/microsoft.public.internetexplorer.general/browse_frm/thread/80a2645dc598a4b2?pli=1" target="_blank">很早就有人</a>发现过这个问题了，而且还有一个专门的网站（<a href="http://crashie8.com/">http://crashie8.com/</a>，请谨慎传播此链接）。除了IE以外的浏览器打开那个页面都不会有任何问题，但是IE却莫名其妙地崩溃。</p>

<p>当然，如果你细心的话，会发现上面的HTML代码中<code>&lt;form&gt;</code>标签没有闭合。我们需要在<code>&lt;input&gt;</code>标签后使用<code>&lt;/form&gt;</code>闭合标签才能让这段代码符合规范。规范的代码就不会让IE崩溃了，这样看来似乎并不是IE的错。</p>

<p>但是，<strong>当其他浏览器都能有很高的容错性时，IE没有，甚至到了IE9还是没有，这就是IE的悲剧了。</strong></p>

<p><strong>
</strong>
<blockquote>编译自How-to Geek: <a title="How to Crash Any Version of Internet Explorer with Simple HTML" href="http://www.howtogeek.com/howto/33394/how-to-crash-any-version-of-internet-explorer-with-simple-html/" target="_blank">How to Crash Any Version of Internet Explorer with Simple HTML </a></blockquote></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Web开发中初学者容易混淆的9个命名约定]]></title>
    <link href="http://blog.dayanjia.com/2010/10/9-confusing-naming-conventions-for-beginners-in-web-programming/"/>
    <updated>2010-10-26T00:00:00+08:00</updated>
    <id>http://blog.dayanjia.com/2010/10/9-confusing-naming-conventions-for-beginners-in-web-programming</id>
    <content type="html"><![CDATA[<p>当人们一开始接触各种Web开发语言时，总会发现彻底搞懂不同语言的命名约定是一件很要命的事情。而且当开发者在争论什么才是最佳实践时，事情会变得更加让人困惑。为了让初学者更容易地在不同语言中过渡，这篇文章列出了一些常见的约定。</p>

<p><!--more-->
<h2>1. 类属性名前加下划线</h2>
如果你看到一个变量或者方法是以_开头的，其实这并不代表其幕后有什么猫腻。这仅仅是为了提醒开发者这个变量/属性/方法是私有的（<code>private</code>）或是受保护的（<code>protected</code>），它们不能从类的外部访问到。
<h4>PHP方式</h4>
[php]class MyClass {<br />
   // 这个实例变量在类外部无法访问<br />
   private $_someVariable;</p>

<p>   // 这个方法仅在类内部或者其它继承它的类中可用<br />
   protected function __behindTheScenesMethod() {}<br />
}[/php]
<h4>JavaScript方式</h4>
[javascript]var Female = (function() {<br />
   var _trueAge = 50,<br />
        _trueWeight = 140;</p>

<p>   return {<br />
      age : _trueAge - 15,<br />
      weight : _trueWeight - 30<br />
   };<br />
})();</p>

<p>Female.age; // 35<br />
Female.weight; // 110<br />
Female._trueAge; // 未定义（因为它是私有的，嘿嘿）[/javascript]</p>

<p>这里的Female并不是一个方法，但它返回一个对象（译注：这个例子很幽默）。这样，下划线前缀就可以提醒我们哪些是私有的。
<h2>2. 大写常量名</h2>
常量（<code>Constant</code>）代表了一个不会改变的静态值。例如，假设一个项目中需要一个税率值.0825，它便是一个常量。然而，并非所有语言都支持这种类型。因此，最好的做法是全部使用大写字母来提醒自己这是个常量。在JavaScript中，语言内建的对象都是采用这种约定，例如<code>MATH.PI</code>。
<h4>JavaScript方式</h4>
[javascript]var TAXRATE = .0825;[/javascript]
<h4>PHP方式</h4>
[php]define(&#8216;TAXRATE&#8217;, .0825);[/php]
<h2>3. 单字母前缀</h2>
你一定在某些场合见过变量名是以一个单独字母开头的，例如“s”或者“i”。</p>

<p>[php]$sName = &#8216;Captain Jack Sparrow&#8217;; //译注：Jack Sparrow《加勒比海盗》中的船长角色[/php]</p>

<p>这被称作“匈牙利命名法”，事实上在近几年并不流行。不过在许多公司内这仍是一条约定。
<blockquote>匈牙利命名法能够提醒开发者它正在使用的变量是什么类型的：<code>string</code>，<code>integer</code>等。</blockquote>
尤其在JavaScript中，这种方法是不可取的，因为JavaScript是弱类型的语言。弱类型语言不需要在使用变量前声明它的类型。如果我们给一个字符串以“s”开头命名，但是后来我们把一个整数赋值给它，这种命名法还有什么意义呢？事实上，这种形式的命名法会妨碍我们的工作，而不是对工作有益。</p>

<p>[javascript]var sName = &quot;Lieutenant Commander Geordi La Forge&quot;; // 译注：Geordi La Forge 少校是《星际迷航：下一代》中的角色，天生眼盲，佩戴高科技护目镜<br />
typeof(sName); // string<br />
&#8230;.<br />
sName = undefined;<br />
typeof(sName) // undefined[/javascript]
<h3>美元符号</h3>
致jQuery使用者：当你使用工厂函数生成对象并且心里暗喜没有受到匈牙利命名法的束缚时，你是否会在变量名前加一个美元符号？如果是这样，那么这也是匈牙利命名法的一种形式。这个符号的唯一目的是提醒你这个变量的类型。</p>

<p>[javascript]// 这个美元符号提醒我可它可以使用jQuery的各种方法<br />
var $container = $(&#8216;#container&#8217;);<br />
[/javascript]
<h4>应当这么使用吗？</h4>
这完全取决于你自己。事实上许多jQuery团队的成员也使用这种美元符号前缀。使用它的最终目的是能够适应你的工作。以我而言，几年前我就不使用这种美元符号了——这仅仅是因为我个人觉得这没有必要。你可以自己决定如何对待它。
<h2>4. 首字母大写</h2>
下面这个首字母大写的变量名看上去如何？</p>

<p>[php]$response = $SomeClass-&gt;doSomething();[/php]</p>

<p>在上面的代码中，$SomeClass写成了单词首字母大写的形式是因为它是一个类的对象而不是普通的变量名。通常这是绝大多数开发者使用的方法。当我们一年后再回过头来看这些代码时，这个简单的形式就能准确地告诉我们这是一个对象，可以调用其方法。</p>

<p>[php]// 注意类名中大写的M<br />
class MyClass {<br />
   function __construct() {}<br />
}[/php]
<h3>JavaScript方式</h3>
在JavaScript中，没有真正的类，但是有构造（<code>constructor</code>）函数。</p>

<p>[javascript]var Jeff = new Person();[/javascript]</p>

<p>我们将构造器（<code>Person</code>）的首字母大写的原因是我们有时候很容易忘记new关键词。在这种情况下，JavaScript往往不会抛出任何警告，但是这个小错误却非常难调试到。首字母大写能够给开发者在debug时一个有效的提示。Douglas Crockford是这个约定最大的提倡者。（译注：Douglas Crockford是Javascript社区的大神级人物，是<a href="http://www.json.org/json-zh.html">JSON</a>、<a href="http://www.jslint.com/">JSLint</a>、<a href="http://www.crockford.com/javascript/jsmin.html">JSMin</a>和<a href="http://www.adsafe.org/">ADSafe</a>之父，是《<a title="JavaScript: the good parts" href="http://books.google.com/books?id=PXa2bby0oQ0C&amp;printsec=frontcover&amp;hl=zh-CN&amp;source=gbs_ge_summary_r&amp;cad=0#v=onepage&amp;q&amp;f=false" target="_blank">JavaScript：The Good Parts</a>》的作者。）</p>

<p>另一种可选方案是，如果你生怕自己的健忘，你可以在执行前首先确保构造器存在并且正确。</p>

<p>[javascript]function Person(name) {<br />
  // 如果遗漏了new关键词，将会执行下面的构造函数并返回一个新的实例<br />
  if ( this.constructor !== Person ) {<br />
    return new Person(name);<br />
  }<br />
 this.name = name;<br />
}</p>

<p>// 故意不用“new”关键词<br />
var Joey = Person(&#8216;Joey&#8217;);<br />
Joey.name; // Joey<br />
[/javascript]
<h2>5. 驼峰式和下划线式</h2>
为什么有些变量使用驼峰式大小写模式，而有些是使用下划线分割单词呢？哪一种是正确的方法？答案是：没有绝对正确的用法。这完全去取决于你的语言，或者你公司的代码约定。这两种都是非常好的方式。</p>

<p>[javascript]// camelCase<br />
var preacherOfSockChanging = &#8216;Lieutenant Dan&#8217;; // 译注：Dan中尉，电影《阿甘正传》中的人物</p>

<p>// under_score<br />
var preacher_of_sock_changing = &#8216;Lieutenant Dan&#8217;;[/javascript]</p>

<p>正如大家所说，跟随你使用的语言的通常约定是一项最佳实践。例如，大量JavaScript开发者使用驼峰式的语法，而像PHP则更倾向于使用下划线分割。需要重申的是，这不是板上钉钉的规矩。Zend框架就是驼峰式大小写的推动者。
<blockquote>比你使用什么更重要的是确保你一直去使用它！</blockquote>
<h2>6. 目录结构</h2>
通常在团队中工作时，你必须和你的协同开发者一起接受一个合适的目录结构。最基本的显然是不要把所有的样式表、脚本都放到项目的根目录下，这显得非常无组织无纪律。许多开发者愿意将它们的图像、脚本和样式表放到一个<code>Assets</code>目录下。</p>

<p>[code]/ Project Root<br />
  /Assets<br />
    / js<br />
      / min<br />
        script_min.js<br />
      script.js<br />
    / css<br />
      / min<br />
        style_min.css<br />
      style.css<br />
    / img<br />
      img1.jpg<br />
  index.html<br />
  otherFile.html[/code]</p>

<p>此外，记住一项创建<code>min</code>目录的约定规则。这里面动态地存放着压缩后的脚本和样式表文件。
<h2>7. 语义</h2>
当使用标记语言时，需要明白你选择的<code>id</code>和<code>class</code>需要能够描述你的内容，而不是仅仅代表形式。例如：
<h4>最悲剧的</h4>
[html]&lt;div id=&quot;middle-left-and-then-a-little-lower&quot;&gt; Justin Bieber is my homeboy section. &lt;/div&gt;[/html]
<h4>稍好一些</h4>
[html]&lt;div class=&quot;section&quot;&gt; Justin Bieber is my homeboy section. &lt;/div&gt;[/html]
<h4>最佳做法</h4>
[html]&lt;section&gt; Justin Bieber is my homeboy section. &lt;/section&gt;  [/html]</p>

<p>怎么样？如果在六个月以后，你决定把Justin Bieber粉丝的内容放到中间偏右并稍低一些（middle-RIGHT-and-then-a-little-lower）的位置呢？这样，你的i<code>id</code>就完全失去了意义。
<blockquote>我们正在向HTML5的世界过渡，你应当在元素中使用尽量少的标识。<code>id</code>显得很不灵活，而且很多情况下并不需要。</blockquote>
<h2>8. 写两遍<code>Header</code>和<code>Footer</code></h2>
当做一个居中的网站时，需要将多重背景扩展到整个窗口的宽度（通常是<code>header</code>和<code>footer</code>），是非常令人讨厌的。你通常需要包裹你的内容，这样外层元素扩展后，内层元素还能继续保持居中。</p>

<p>这是一个一般性的问题，在创建必需的标记语言内容时接受这样的约定规则是很重要的。</p>

<p>[html]&lt;div id=&quot;footer-wrap&quot;&gt;<br />
	&lt;footer&gt;<br />
	  My footer content goes here.<br />
	&lt;/footer&gt;<br />
&lt;/div&gt;[/html]</p>

<p>难以决定的是，假设你在使用HTML5中的新元素，你需要决定将<code>footer</code>标签放在里面还是外面。这个还有待讨论。然而我个人觉得将<code>footer</code>放在内层更加语义化。</p>

<p><code>div</code>只限下面的情况时才使用：
<ul>
	<li>没有更好的元素来使用了</li>
	<li>你需要一个单纯的表示布局结构的元素</li>
</ul>
<h2>9. 简写</h2>
现在就决定是否允许在你的代码中使用简写。书写精确且整洁的代码总是一场可读性和代码长度的斗争。这也正是开发团队内使用相同代码风格是首要考虑规则的原因。来看两个简单的例子：
<h4>三目运算符合适吗？</h4>
[javascript]var name = &#8216;Joe&#8217;;</p>

<p>// regular<br />
if ( name === &#8216;Jeff&#8217; ) {<br />
  alert(&quot;That&#8217;s me&quot;);<br />
} else {<br />
  alert(&quot;Nope&quot;);<br />
}</p>

<p>// ternary<br />
alert(name === &quot;Jeff&quot; ? &quot;That&#8217;s me&quot; : &quot;Nope&quot;); // Nope[/javascript]
<h4>使用逻辑<code>&amp;&amp;</code>来写简短的条件判断句？</h4>
[javascript]var getTweets = true;</p>

<p>// regular<br />
if ( getTweets ) {<br />
 alert(&#8216;getting them now&#8217;);<br />
}</p>

<p>// 逻辑 AND<br />
// 除非左边是“true”否则右边不会运行<br />
getTweets &amp;&amp; alert(&#8216;Getting them now&#8217;);[/javascript]</p>

<p>许多开发者都对如此使用AND感到不爽，坚持认为这影响了可读性。尽管这的确是是一个有力的观点，但是连流行的框架类库例如jQuery也大量使用了这种方法。
<h2>结论</h2>
再次重申，你在开发时坚持使用某种方式比选择什么样的的约定来的更加重要。事实上，许多开发团队都有自己专门的代码风格指导给新开发者参考。感谢阅读！
<blockquote>译注：关于变量命名更详细的约定规则介绍，大家可以阅读《代码大全》（第2版）的第11章。</blockquote></p>

<p>原文：<a title="9 Confusing Naming Conventions for Beginners" href="http://net.tutsplus.com/articles/general/9-confusing-naming-conventions-for-beginners/" target="_blank">9 Confusing Naming Conventions for Beginners</a>
作者：<a title="Jeffrey Way" href="http://net.tutsplus.com/author/jeffreyway/">Jeffrey Way</a></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[有些Wordpress插件，你伤不起……]]></title>
    <link href="http://blog.dayanjia.com/2010/10/wordpress-plugin-conflict/"/>
    <updated>2010-10-17T00:00:00+08:00</updated>
    <id>http://blog.dayanjia.com/2010/10/wordpress-plugin-conflict</id>
    <content type="html"><![CDATA[<p>熟悉Wordpress的朋友都知道Gravatar，这是一个号称“全球通用头像”的服务。用户只需要在Gravatar网站上用E-mail注册后上传一个头像，今后在支持这项服务的网站上就可以直接根据那个E-mail获取自己的头像，无需在其他网站再次上传图片。Wordpress内置了对Gravatar的支持，许多国外的网站和开源建站程序例如<a title="Drupal" href="http://drupal.org/" target="_blank">Drupal</a>都可以集成这项功能，就连国内的Discuz!也有Gravatar的插件。但是令人不解的是，Gravatar于日前荣获国家级防火墙金牌认证，这使得无数Wordpress中的用户头像变成了一个个小红叉，看了颇不舒服。为此，有人专门制作了网站服务器中转Gravatar头像的插件。为了使自己博客的页面显得更加完美，我义无反顾地安装了它，于是，悲剧就这样开始了……</p>

<p><!--more-->
<h2>悲剧的开始：WP Gravatar Mini Cache</h2>
看到网上有朋友写了这个<a title="WP插件Gravatar-Mini-Cache" href="http://www.evlos.org/2010/01/25/my-first-plugin-mini_gravatar_cache/" target="_blank">Gravatar缓存的插件</a>，于是便下载安装，激活后便没有多管。没想到过了一会儿打开我的博客，居然打不开了，ping一下也是很高的延迟，还会有丢包。这时我的第一感觉是，VPS的线路又抽风了。但是当我多次刷新页面后，居然给了我一个HTTP 500错误，于是立马感觉事情没这么简单。</p>

<p>用SSH登录到VPS，我先用<code>uptime</code>看了下系统负载，乖乖，居然1min、5min、15min的平均负载达到了两位数，不卡才怪啊！于是又用<code>free -m</code>看看内存，512MB的内存基本全部被吃掉了，交换区也用掉了将近五分之一。这到底是神马情况？</p>

<p>事实上，我一开始并没有想到是这个插件的原因，我想，不如先重启一下VPS试试看吧。重启以后情况依旧，看了看占用CPU资源和内存的几个进程，无一例外都是各种php-cgi。这个时候我才想起来去看Apache的日志。赶紧去<code>/var/log/httpd</code>去看<code>access_log</code>和<code>error_log</code>。<code>access_log</code>里面全是“<code>"OPTIONS * HTTP/1.0" 200 - "-" "Apache/2.2.3 (CentOS) (internal dummy connection)"</code>”这句话，不知道什么意思。于是又去看<code>error_log</code>，那里面基本上是三行报告内容的重复：</p>

<p>[code light=&#8221;true&#8221;][Sun Oct 17 14:32:52 2010] [warn] mod_fcgid: process 8078 graceful kill fail, sending SIGKILL<br />
[Sun Oct 17 14:32:58 2010] [notice] mod_fcgid: call /home/dayanjia/public_html/index.php with wrapper /home/dayanjia/fcgi-bin/php5.fcgi<br />
[Sun Oct 17 14:32:58 2010] [notice] mod_fcgid: process /home/dayanjia/public_html/index.php(8058) exit(communication error), get stop signal 9[/code]</p>

<p>各种进程僵尸……这就让我更加摸不着头脑了……完全不可捉摸的错误报告啊……这时候我猛然想起那个插件，到插件目录下删掉它，再看系统负载，果然恢复正常了，网页也可以正常打开了。
<h2>插件连锁反应</h2>
这件事本来就这么结束了，但是文章开始提到的Gravatar头像无法显示的问题还是没有解决啊。看了看那个引起问题的插件的代码，短短十几行，在<code>get_avatar()</code>上加了一个Hook而已，怎么会造成这么严重的问题呢？于是到网上Google，找到<a title="新版 Gravatar 緩存" href="http://kan.willin.org/?p=1320" target="_blank">另一个方法</a>，按照作者的方法，在主题函数中新加一个<code>my_avatar</code>，然后把<code>get_avatar</code>全部换成<code>my_avatar</code>，避免增加Hook。按照此方法一试，果然在日志的评论页面能够正常显示头像了。</p>

<p>但是事情还没有结束。我在侧边栏上使用了一个<a title="最新评论插件 WP-RecentComments" href="http://www.neoease.com/wp-recentcomments/" target="_blank">WP-RecentComments</a>插件，那里面也会显示头像。于是我去编辑插件的源码，也按照上面的方法替换了get_avatar函数。于是当我再一次刷新的时候……悲剧又重现了！</p>

<p>难道罪魁祸首是WP-RecentComments这个显示最新评论的插件？这个时候我又想到，我还安装了<a href="http://www.linkedin.com/in/w3edge" target="_blank">W3 Total Cache</a>这个缓存插件，把缓存插件禁用后，居然又可以正常打开首页和日志页了！这还真是扑朔迷离啊……事实上，能够引起整站HTTP 500错误的，估计也有这样的缓存插件能干的出来。</p>

<p>我禁用了这个缓存插件，还原了WP-RecentComments代码，又重新安装了Gravatar Mini Cache。打开网站，一切正常！接着我又去重新启用W3 Total Cache并重新进行了配置。终于现在它们能够相安无事了……头像也能正常显示了……
<h2>结论</h2>
总结一下，这次的事件由三个插件连锁反应产生。由于Gravatar Mini Cache给<code>get_avatar()</code>加了一个Hook，WP-RecentComments在调用头像时就会激活Gravatar Mini Cache的函数，然后W3 Total Cache过来不知怎么的掺和一下，就整站挂掉了。似乎是处理顺序的问题导致php脚本陷入了一个死循环，可以看到VPS上的php进程不断地开启，变成僵尸，然后挂掉……</p>

<p>Wordpress以插件众多，功能丰富著称，但是对于那些喜欢闹脾气的Wordpress插件，你真的伤不起……</p>
]]></content>
  </entry>
  
</feed>

