Stoned's Blog

A startup hacker.

七八月份的打算

七八月份有几件事情想做:

  1. [已完成]做一个极客公园的火箭发射彩蛋,demoGithub地址演示地址
  2. 利用手机的传感器做一个提醒带手机的应用,让我老婆用用,她老往忘带手机。
  3. 将之前想过的想法实现,地铁情缘,用蓝牙做简单的应用。(@焕德 一起hack一下?)

这一年

一年前的大概这个时候,我正好从超图离职,加入创新工场二期助跑团队——邻伴。在半个月之前我的女儿刚好出生,紧接着@焕德 给我打电话邀请我加入他们。那一刻,我的人生仿佛展开了一条崭新的道路,我梦寐以求的道路。

时间再回到去年的3月份,那是我最困惑的时候,项目已做到让我厌倦,想走,却又没想好要做什么。当时恰好部门内要做LBS平台,最开始由我负责技术这块。这个时候,我又重新燃起了热情,一想到自己所学的东西终于能在大众领域里有所应用,那是无比的兴奋。但是好景不长,经过几个月之后,我发现事情并不能如我所愿的进行下去,总有处理不完的其它的琐事,部门也不能投入很多资源来做这个事情,况且我的热情也快消失殆尽了,于是萌发了出去做LBS的念头。于是便进入邻伴。

以上是背景,在崭新的道路面前,有几点让我特别兴奋,

  1. 接触,学习创业。
  2. 能做自己喜欢做的LBS的事情。
  3. 加入创新工场。

而这些兴奋就是我在邻伴时候的鸡血。

在邻伴的日子是令人怀念的,在那里我完整地经历了一个产品的生命周期,完整地经历了创业的失败,也完整地经历了创始人的人间蒸发。在邻伴,我通宵的时间比之前三年都多,那是一段打满了鸡血的日子,我喜欢那样的日子,那仿佛是我青春的尾巴。而我之所以从超图离开,也正是因为我想找到这样的日子。在邻伴,我欣赏@焕德 的镇定,他让我看到了一个leader该有的气度,我也喜欢他不在的时候,我做了我和他两人份的开发的工作,那让我看到我自身的能量。

虽然很遗憾,不管什么原因,邻伴最终失败。但不能否认的是我们的确是一个不错的团队。有两个人说过喜欢我们邻伴的团队,一个是@白斗斗 , 另一个是我老婆。生命中的诸多细节总是充满了暗示的意味,我相信我们那个时候的日子会是一如那个初次聚会的咖啡馆的名字——那是最好的时光。

过了十一,我加入了计划FM团队。加入计划FM主要是因为@李天放 ,毫无疑问,天放是一位极优秀的工程师,我希望从他身上学到一些东西,提高自己开发的水平。事实也是如此,从天放身上我学会了(或者说天放教会了我)诸多东西,而这些东西开发进阶的必经之路。而之前长时间的瓶颈,也正是因为缺乏这些东西。可以这么说,如果不是天放,我的程序观,价值观会大不相同。

天放教会我的第一件事情,便是命名。由于我的英语水平并不好,命名的时候名词动词不分,单数复数不分,所以最开始code review的时候,天放总要帮我改参数、函数的命名。后来在蔡学墉的微博中,我才发现有命名肌这一说法,而命名肌也是每个开发的人必须锻炼的一块肌肉。

命名是第一步,做好命名的目的是提高代码的可读性。代码即注释。这也解决我长期以来的一个困惑,对于一些复杂的逻辑的部分,哪怕我写了非常详细的注释,还是要花很长的时间才能理解代码,而将复杂的逻辑分离,再辅以良好的命名之后,代码立马变得清晰起来。

第三,保持简单,无论是代码还是逻辑都是一样。天放极度推崇简单,推崇甚至有些洁癖的地步。大部分时候,天放是有道理的,因为如果你的逻辑都很难理解,那自然用户也就会很难理解这些东西,而这样做出来的东西多半是不对的,很多时候需要重新去想一条更合适的路出来。

第四,保持简单的一个最佳检验办法就是写单元测试,如果一段代码很难写出覆盖全部case的测试,那么这段代码很可能是有问题的,要么代码太多复杂,要么这个类有问题。这种时候我们就需要重新考虑这些代码了。

恰好这几天,我正在看@焕德 留给我的《重构》这本书,看了书我才发现重构的许多基本思想,我已在天放的潜移默化中学会,掌握,并践行着。天放对我的影响绝不仅限于开发这方面,他让我深刻体会到“你要跟优秀的人一起工作”这话的正确性,也深受其益。

这一年以来对我影响最大的是无疑是对我价值观的修正。在超图的那段时间,我几乎每隔一段时间就要去想,我是要成为项目经理,还是一个优秀的工程师。我可以成为项目经理,事实上我的头衔也是项目经理。但我本能地反抗,因为那不是我加入超图的目的,那不是我的梦想。但一个残酷的事实便是你只能成为一个项目经理,因为那才是工程师在超图的唯一进化方向。我现在可以很清晰地说出那时困惑的原因,那是价值观的冲突。但那时的困惑难以解答,因为我不知道是否存在我所推崇的价值观的公司,仿佛工程师文化的公司只存在于国外。而这一年以来的经历让我相信工程师文化是合理并大有裨益的。而和一群观念相同的人工作,那是一件多么让人愉悦的事情。而每天处于冲突之中,只会耗尽你的潜力。

这一年以来我的第二个收获便是自我管理能力的提升,工作效率的大幅提高。在这之前,我的人生似乎总存在着一个悖论,像我这样的人究竟能不能做出一些成就。因为我可以吃苦,可以非常勤奋,但这一切的基础便是我一定要有极大的热情,如果我没有热情,那么我的工作很可能是工作效率及其低下的。在邻伴也是这样的轨迹,我可以像打了鸡血一般疯狂工作,但鸡血毕竟有限,你不可能永远保持这样的状态,所以在邻伴的最后一个月,无论我的状态还是效率都已经非常糟糕。我总是跳不出这样的怪圈,所以之前我只能是不停地寻找我的兴趣点,然后靠着这兴趣提高我的工作效率。所以我的工作状态总是一阵一阵的,挺不稳定的。

但这种状态在今年改善不少。很多时候我更加了解自己的状态,我可以也调整自己的状态,甚至有些时候我还能强迫自己进入状态。所有这一切归功于天放给我介绍的番茄工作法,那真是算自我管理的神器了吧。

如上所述,这一年以来我的改变不少,也对自己今后要走的路更加清晰,少了份困惑,多了些信心。别人说25岁是个尴尬的年龄,我同意这个观点,不过我很快就不尴尬了:)

博客@工具

我想到一个想法,就是可以在博客中@别人的微博,然后别人就可以在微博中收到你的@,我在网上找了一下,没找到合适的工具,所以我就自己写吧。

链接

  1. 博客@工具
  2. github

马尼拉终于可以放到网上了

一个hackday的游戏之作(天放、崔毅、我),雏形只花了半天时间,但收尾工作却花了我一两个月的时间,原因是

  1. 代码维护难度较高
  2. 恒心与耐心不足。拖拖拉拉导致那么久才做完beta版本。

马尼拉地址

节约时间就是在浪费生命?!

想到这个命题,挺奇怪的。生命的意义究竟是什么?从微小的受精卵,到懵懂的孩童时期,再到化为尘土。人终究是在这个世界上留不下什么东西的。所有最后成空,那普世的价值观又有什么意义呢?那既然如此,人为什么要去节约时间?

我想这一定有一个悖论,人应该去做自己想做的事情,而节不节约时间是自己的决定,这不是价值标准。就像马斯洛的人的5个阶段,我想那也是在普世的价值观下的金科玉律,跳出这个价值体系,那唯一存在的就是存在本身吧。

很有意思,存在本身也是多少小说家,哲学家热议的东西,或许存在对于每个人的意义就像诗歌一般,每个人都是自己的诗人。

与存在对应的就是救赎与信仰,存在如同一道难题,而解开这道难题的,很多时候人们用的就是信仰。我不排斥信仰,但我还没建立起自己的信仰,所以我才那般害怕,那般恐惧。

存在对于存在本身,或许这才是我们要用一生去解答的终极难题。

戒Dota两周

戒Dota、微博、口袋妖怪两周,空闲时间看看书,看看王小波。

Ejabberd使用ruby做用户验证(extauth_program)

背景

使用ejabberd做聊天服务,最大的问题是怎么跟现有的系统对接,其中最重要的就是用户系统如何对接。而这一点,ejabberd已经为我们考虑到了,为了方便不会使用erlang的开发者使用自己的用户系统,ejabberd提供了extauth_program这个配置,从而可以使用任何可执行的脚本作为用户系统的接口。其中perl语言的demo在ejabberd srouce的example里就有。而更多的配置可以参考Authentication Scripts,你可以使用非常多的语言和数据库,包括perl,python,甚至php,c#。其实你用任何语言都没有关系,因为毕竟只有短短几十行代码,你唯一要做的就是设置好你的数据库参数。

我的尝试步骤

但是不幸的是官方给的参考里是没有ruby脚本的(有脚本,但是页面已经失效),于是乎我在网上狂搜extauth ruby的实现,还好找到一个据说有点问题的ruby代码ejabberd not exec my script ,那人就是因为执行不了所以才发帖求助的。于是

  1. 我copy了他的代码,然后稍作了修改,配置好extauth。果然执行不了,没有任何反应,连我设置的log都没有输出来。而ejabberd的log是extauth 脚本意外终止,原因是normal
  2. 于是我做了个测试,将所有的代码都删掉,只保留log的部分,结果还是没有log,而我用ruby执行是有log
  3. 这让我一度怀疑ejabberd不支持ruby,于是我换用ejabberd source里的perl脚本,结果一切工作正常。
  4. 我误认为ejabberd不支持ruby,于是我放弃ruby的方式,在网上down了Authenticate Against MySQL with Perl,修改后run,结果发现跟1的结果是一样,无法运行。
  5. 既然有一个正确的,一个错误的相同语言的脚本,那我就可以比对哪里导致运行失败了。比对的结果是4下载的sql没有执行权限,于是我chmod a+x,4运行正常了,但我仍然不能正确登录。
  6. 在perl console环境中一步步执行4的脚本,发现原因是我缺少sql lib的支持。于是准备换用python脚本。
  7. 在修改python脚本支持的时候,我意外发现所有的文件开头都有一行#!/usr/bin/perl这样的注释,我突然明白为什么我的ruby脚本不能执行了,因为它不知道怎么去执行,于是在ruby文件开头加上了#!/usr/bin/env ruby,这个世界顿时清净了。后来想起我好像在鸟哥的私房菜里边见过这些东西,后悔看得太草了。

要点

  1. 你的script本身要能正确执行,即有执行权限,而且是可执行的(第一行有类似#!/usr/bin/env ruby的注释)
  2. 正确配置extauth。如下: {auth_method, external}.%%这是要点 {extauth_program, “/var/lib/ejabberd/db_auth.rb”}.
  3. 没有第三了。

下载

a ejabberd extauth script write by ruby

参考资料

  1. extauth list
  2. ejabberd not exec my script
  3. ejabberd extauth using erlang escript

下个月的打算

从三月开始,到现在两个月的时间。期间想做太多的事情,很多事情起了个头,就放在那里了,所以决定从5月开始收一收了。做完手头上想做的事情。这些事情分别是:

  1. 12306 自动登录【闲置】
  2. 英语阅读工具【放弃】
  3. 妈妈说——为我弟做的语音记录提醒Android软件,5月6号之前完成【done】
  4. 马尼拉——html5游戏【done,完成基本版本】
  5. 图图的blog【闲置】
  6. 在家里——给老婆做的东西,还没开始做,6月份再做吧。【看情况】

Ejabberd部署

我们选用ejabberd作为我们聊天的Server,原因是可以1、erlang性能高2、用得人多。 经过两天的折腾,我终于在三台不同的系统上都装上了ejabberd,分别讲讲这三个平台的安装经历。

Mac

ejabberd有Mac下的dmg安装包,有可以通过source安装。

  1. 我的第一次尝试是直接使用了ejabberd的dmg安装包,结果执行postinstall脚本的时候出错,服务起不来。没有具体的错误提示。
  2. 所以换用source安装的方式。先装的erlang 15B01,然后安装ejabberd 2.1.10版本。结果使用/sbin/ejabberdctl start 没有任何反应。通过netstat看发现没有5280端口。于是换ejabberdctl live命令,发现提示是端口冲突。后来想起来我装过openfire,于是卸载掉,重新启动,还是出错。
  3. 后来发现erlang 15B01出的比ejabberd2.1.10还晚,怀疑是版本问题。于是卸载erlang 15B01,换用erlang 15B。
  4. 装好之后,重新安装ejabberd,好像就没什么大问题了。
  5. 启动访问http://localhost:5280/admin%E3%80%82%E6%98%AF%E5%90%A6%E8%83%BD%E5%87%BA%E6%9D%A5%E9%A1%B5%E9%9D%A2%E3%80%82

Cent OS

直接用source安装的,安装完之后没什么问题,有问题可以参考下面的资料。

Ubuntu 10.10

  1. 我首先是直接用apt-get安装的erlang,结果一看,版本是5.9.1就是15B01,所以就卸载掉了。
  2. 后来手动安装erlang失败,发现缺少依赖库
  3. 所以必须先安装下面的东西
  4. sudoapt-get -yinstall\ build-essential m4 libncurses5-dev \ libssh-dev unixodbc-dev libgmp3-dev \ libwxgtk2.8-dev libglu1-mesa-dev \ fop xsltproc default-jdk
  5. 安装完erlang后,又手动安装了ejabberd,结果启动总是不成功。
  6. 后又换apt-get安装ejabberd,启动时提示几个文件有权限问题,我直接chmod 777就搞定了。
  7. ubuntu中启动ejabberd是这样启动的,/etc/init.d/ejabberd start。不要用ejabberdctl,会失败。

排错指南

  1. 在start之前检查下epmd, beam, beam.smp等是否启动了,如果启动着,先把他们杀掉,他们是erlang相关的东西。
  2. 检查是否有应用占用了5222,5223,5280等端口,5222端口是xmpp协议本身规定的,所以默认是用此端口。如果之前装过其他的XMPP server请停止或卸载,比如OpenFire
  3. ejabberd有很重的erlang依赖关系,所以一定要对好版本,参考下面的资料。
  4. 如果不需要配置,也是可以启动服务的,所以无法启动不是配置的问题。
  5. 如果start没什么错误提示,可以改用status或者live方式启动,这样错误会明显一点,如果还是没有错误提示,只能到/var/log/ejabberd底下去看了。

参考资料

  1. ejabberd 简明配置
  2. ejabberd 简明配置2
  3. ejabberd清理
  4. ejabberd清理2
  5. Installing Erlang/OTP R14B04 on Ubuntu 11.10 from source tarball
  6. E: Sub-process /usr/bin/dpkg returned an error code 解决办法
  7. odbc : ODBC library – link check failed
  8. Debian package difference
  9. installing-erlang-on-mac-os-x
  10. ejabberd与erlang版本对应关系(重要)
  11. ejabberd安装配置指南(不太有用)
  12. Debugging ejabberd

漫漫破解路

前言

11年是个值得纪录的时刻,12306的推出改变了人们购买火车票的方式,以前是挤火车站,现在是挤12306。于是在这个中药的时刻,各路人马各显神通,自动登录,自动购票,自动刷票等应运而生。最著名的要数zzdhidden的项目,一时间不管IE,firefox,Chrome,只要是想要买火车票的,基本都装上了那个脚本,好不风光。

但好景不长,由于铁道部发现了验证码不刷新的漏洞之后,以上方法通通失效。但我还是不甘心,我还是希望能够有一个东西至少能实现自动登录。

思路

铁道部的验证码其实并不算复杂,我觉得只要能对它进行OCR,我应该就能实现自动登录。我特意在在线OCR的网站上进行了测试,结果还是比较理想的,自动登录的验证码是可以被识别的。于是就进行了漫漫破解路,我到目前为止还是不清楚,最终能不能成功。

难度

铁道部用了HTTPS的方式访问,连图片也是HTTPS的方式,而且在上回修复验证码不刷新的问题之后,验证码已经没有可能性越过,只能硬碰硬的方式进行破解了。

尝试

第一次

我的第一次尝试确定验证码跟的确定跟什么有关,cookie?page?js的隐藏的东西?还是说通通相关。我的做法是打开浏览器,打开登录页面,再打开另外一个页面,再输入验证码的地址。然后在之前打开的登录页面中输入后一个页面的验证码,结果登录成功。由此可以得出结论,cookie只跟session相关,跟page是无关的。

第二次

第二次尝试,是测试是否只跟session相关。我在Chrome中打开登陆界面,然后用ruby模拟同一个session去抓取验证码。结果却很让我失望,登录失败。所以,由这个试验可以得出的是因为是HTTPS的协议,在每次连接的时候就默认有个session的概念,所以用cookie的那个session是模拟不了的

接下来

在经历了前两次的试验之后,我觉得还剩下两条路:

  1. 获取cache中的图片,然后传给验证码破解服务网站,得到验证码之后,提交
  2. 做一个https代理,拦截验证码的请求,破解后传给用户。

从用户的角度来讲,我觉得用户很难相信一个代理,第二如果真以这种方式实现的话,我的服务器压力也受不了。所以我优先去尝试第一种方式。于是就有了我的第三次尝试。

第三次

用户缓存中的图片。通过搜索,发现在IE下可以用cache api,或者可以将图片拷贝到剪切板。Chrome下可以通过访问chrome://cache/。但怎么把这些图片传给我的服务器呢?我觉得无论是IE还是Chrome,肯定有权限的限制,于是没往这条路上深入。

第四次

还是想要在客户端传图片给我的解析服务。于是想到了HTML5的本地存储。于是在测试之后发现这个办法还是不可行。因为碰到了安全沙箱问题。在调用canvas.toDataURL()时会报SECURITY_ERR: DOM Exception 18错误。

1
2
3
4
5
6
7
  var img = new Image();   // Create new img element 
  img.onload = function(){
    // execute drawImage statements here 
    ctx.drawImage(img,0,0);
    var imgData = canvas.toDataURL("image/jpg");;
  };
  img.src = 'https://dynamic.12306.cn/otsweb/passCodeAction.do?rand=lrand'; // Set     source path

第五次

但突然峰回路转,后来我分析了沙箱问题的原因是我在本地文件做的测试,而图片的域名是12306的,所以会造成跨域的问题。所以后来我小测试了一下,在Chrome的插件Script里执行上述代码,结果发现不再出现上述错误了。解决这最头疼的跨域问题以及图片获取问题之后,接下来的问题就是怎么把图片数据传到我的OCR服务器了。

于是我也是先做了个简单的试验,将获取到的base64数据post到我的OCR服务器,OCR服务base64解码,再存成文件。我一看是张完整的完整的图片,高兴坏了。

第六次

但当我把OCR的结果传回到客户端时,意外发生了,客户端怎么也没有收到。通过抓包显示,post请求被cancel掉了。后google之,发现正是js的跨域问题,即一个站点的js无法请求另一站点的js的数据。幸好这块都有成熟的解决办法,通过伪装成Script请求的方式进行数据提交。或者直接就用JSONP的方式。这个方式有一个问题,就是不能使用POST方式进行数据的提交了。但图片的大小(base64)有4K以上,怎么才能把图片完整地传到服务器端呢?(因为get有2K的大小限制)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
  var JSONP = document.createElement("script") ;
   JSONP.onload = JSONP.onreadystatechange = function(data){
        if (result.length == 4){
             $("#randCode").val(result);
             submitForm();
        }else{
             reLogin();
        }

        // alert(result);
   }
   JSONP.type = "text/javascript";
   JSONP.src = "http://localhost:3000/ocr";
   document.head.appendChild(JSONP);

第七次

get方式提交我想了两个办法,一是多次分批提交数据,服务端再进行组合。而是利用多个cookie进行提交,这样只用一次请求就可以传完了(cookie的长度限制是4K)。但我在进行cookie测试的时候,失败了,原因是我在12306的域名下无法访问另外一个站点的cookie。所以只能采用第一个办法。

第八次

那下面就是苦力活了。只要传好数据,ORC之后回传给浏览器,再进行登录就好了。我在做这一步的时候碰到挺多问题的。

  1. 是jquery的getJSON方法总是无法触发callback方法,不知何故。没办法,我只能用complete方法代替callback方法进行触发。
  2. 由于是分批次的上传图片数据,要注意图片的顺序问题。所以应该是一个接一个地传。
  3. ruby的默认session同样有4K的大小限制,因此需要改成数据库的方式进行sesison的存储。http://www.cslog.cn/Content/ruby_on_rails_sessions/ https://github.com/nmerouze/mongo_session_store/issues/8
  4. 提交之前需要encodeURIComponent一下

结束

至此,破解12306从设想变成了现实。其实回过头来看看,其实也不是很难,但充满了惊险刺激。比大学时做ACM的题目更加兴奋。

Github:http://github.com/huangxiangdan/12306