Gmail - Tasks

2009-6-3 23:04 | by 2ndboy

  找合用的 online to-do list 已经找了很久,其实我只想要一个备忘形式的简单 to-do list,够用就行,不过找到的不是太复杂就是访问速度不理想,偶尔找到一个简单够用的,结果对中文的支持不够好。

  今天改 Gmail 帐号密码的时候偶然到 Labs 里逛了一圈,发现排在第二的就是一个 to-do list - Tasks,简单好用,支持多个 list,就是它了!

Gmail Tasks

  Google 是了不起的公司,创造了这么多好用的 online service,如今我是 GmailGoogle DocsGoogle Reader 的重度使用者,前几天又登记了 Google Wave 的试用,Wave 的 demo 非常惊艳,希望可以早日开用!

《Lost》Season 5 完结

2009-5-24 10:53 | by 2ndboy

  随着 Juliet 眼前的一道白光,陪伴了《Lost》迷们又一季的 Season 5 结束了,这之后又是大半年的漫长等待!从官网上的宣传来看,Season 6 就是《Lost》的最后一季了,虽然还有一年时间可以期待,但是还真不舍得她就这么结束:)

  两年前在看 Season 3 结尾的时候,我已经猜到了棺材里躺着的是 Locke,但是没想到他回到岛上又“复活”了,而本季结尾的时候我也才知道,“复活”的 Locke 原来是黑衣人变的!
Lost

  所以,看来其实《Lost》中所发生的一切都是白衣人 Jacob 和黑衣人 xxx 的一场较量,或者说是他们的一个赌。有点类似撒旦跟上帝打赌,所有人类在来到伊甸园以后都要不可避免的堕落,上帝说“不,你错了”。

  所有在岛上的人都像是在一个场景中活动的被观察(被测试)对象,他们的行为用来验证到底是黑衣人错了,还是白衣人错了。但是黑衣人最终借助被测试人 Locke & Ben 除掉了 Jacob,当然 Jacob 也不是没有留一手,下一季中 Ilana & Richard 应该会发挥作用(Richard 之前的表现也太像个摆设啦)。期待ing……

MySQL 数据类型的范围上限

2009-5-15 20:15 | by 2ndboy

  今天帮朋友查一个 Discuz! 数据库的异常问题,表现是不能发帖,提示:

Discuz! info: MySQL Query Error
Script: /post.php
SQL: INSERT INTO [Table]threads (fid, readperm, price, iconid, typeid, author, authorid, subject, dateline, lastpost, lastposter, displayorder, digest, special, attachment, subscribed, moderated)
VALUES (’4′, ‘0′, ‘0′, ‘0′, ‘0′, ‘2ndboy’, ‘6′, ‘test’, ‘1242289636′, ‘1242289636′, ‘2ndboy’, ‘0′, ‘0′, ‘0′, ‘0′, ‘0′, ‘0′)
Error: Duplicate entry ‘16777215′ for key 1
Errno.: 1062

  首先排除了程序被修改过的可能,然后看数据库,cdb_threads 表的第一个字段(即 key 1)是 tid,看了一下表里面的数据,最后一个帖子的 tid 确实是 16777215,而表的 auto_increment 值是 16777216,看上去一切正常,Check table 和 Repair table 也后返回 OK。尝试把数据表的 auto_increment 值改成 16777217 也没有用处,真是百思不得其解。

  抱着急病乱投医的心态让管理员更新缓存,还是没用。偶然打开计算器,把 16777215 复制进去,转成十六进制,咦?结果是 FFFFFF,顿时眼前一亮,不会是到达字段所能表示的数值上限了吧?看了一下 cdb_threads 表 tid 字段的定义,是 mediumint(8),经过查看 MySQL 文档得知,mediumint 的长度是 24bits,因此无符号数的最大值正好就是 16777215。这下就小白跟他哥的长的挺相——真相大白啦!

  于是乎备份数据库,把 cdb_threads 的 tid 字段改成 INT 类型,之后发现发帖后 cdb_threads 里出现了新贴的记录,但是界面上还是提示“操作未定义”,怀疑还有跟 tid 有关的字段,经过一番折腾之后,又修改了 n 处 tid 字段,这个世界终于清静啦。

  回过头来看看这个问题,一开始没有马上找到问题本质的原因,实在是对 16777215 这个值的敏感度不够,一般看到 255,65535 这种临界值总会留意一下,但是 24bits 数据的最大值还真没记住,这就是经验啊。

  其次是平时没有深究过 MySQL 里各种数值类型的取值范围(业余的 Web 开发毕竟还是业余的;)),而且对数据类型后面那个括号里的数字有误解,总觉得比如 INT(10) 跟 INT(8) 的取值范围是不一样的,这回查了文档才知道,这个数值是最大显示宽度,跟数据类型的取值范围无关。下面简单摘录一下 MySQL 中各常用数据类型的取值范围:
BIT,位域,6bits,取值范围1~64
TINYINT,很小的整数,8bits,取值范围0~255(有符号数 -128~127)
SMALLINT,小整数,16bits,取值范围 0~65535(有符号数 -32768~32767)
MEDIUMINT,中等大小整数,24bits,取值范围 0~16777215(有符号数 -8388608~8388607)
INT,整数,32bits,取值范围 0~ 4294967295(有符号数 – 2147483648~ 2147483647)
BIGINT,大整数,128bits,取值范围 0~ 18446744073709551615(有符号数 – 9223372036854775808~ 9223372036854775807)

此外,BOOL/BOOLEAN 和 TINYINT 的取值范围相同,INTEGER 和 INT 的取值范围相同。

  这么说来,朋友论坛的帖子数已经过一千六百多万了,现在改成 INT 以后,要到大约四十三亿个帖子时才会再次到达数值上限,那应该已经是很久以后的事情了。

爬山

2009-5-10 21:53 | by 2ndboy

  搬家之后,周末出去逛逛方便多啦,不用在玩的筋疲力尽之后再坐两个小时的公交车回到住处。于是最近两个月里,每到周末就会跟 LP 一起去爬山。杭州得天独厚的条件就在于市中心有西湖,西湖周边又有 n 多大大小小的山,周末去爬山锻炼身体,顺便在天然氧吧里舒缓一下一周工作的疲劳,真的不错。

  昨天 34C,大晴天,我们顶着大太阳继续爬山。虽然西湖周边的山着实不少,但是为了能最方便的达到锻炼身体的目的,我们爬山的路线一直相对比较固定,这样就不用在去爬山和回来的路上花太多时间。这周六从古荡附近上山,途径将军山、美女山、灵峰山、锅子顶,然后到达北高峰,下到法华寺后回家。汗出了不少,水也喝了不少,但是跟刚开始爬山时比确实感觉体力有明显提升,希望可以一直坚持下去,因为——身体是 everything 的本钱:D

《Notting Hill》

2009-5-5 23:02 | by 2ndboy

  上周公司用“国务院规定五·四 28 周岁以下的同学们放假半天”这个通知来宣告我们已经属于中年人行列,所以周五晚上用看爱情喜剧来宣告我仍然属于青年,至少比那些当选杰出青年企业家的老头子们要青年:)

  认识和喜欢朱莉娅·罗伯茨是在《Pretty Woman(风月俏佳人)》,但是这么多年以来看她的片子比较少,算上前不久看的《Erin Brockovich(永不妥协)》,完整看过她主演的片子只有两部而已,但每一部都是精品!

Notting Hill
  《Notting Hill(诺丁山)》是 99 年的老片子,这次也算是终于拜“读”经典了,确实是部好电影,内容虽然是现代童话,但带入感还是非常强烈。我想这部电影之所以经典不仅在于演绎它的是 Julia Roberts/Hugh Grant 和好剧本。其实里面的每一个角色都非常出彩(尤其是 Will 的室友),音乐也非常好听,吐血推荐!!!其实很早就听过片尾那首 Shania Twain 的《You’ve Got A Way》,只是看完电影才知道原来这首歌出自《诺丁山》。看完这么一部好片子之后,字幕刚刚升起,Shania Twain 的磁性嗓音不经意间响起,那感觉,真是没得说……

Audio clip: Adobe Flash Player (version 9 or above) is required to play this audio clip. Download the latest version here. You also need to have JavaScript enabled in your browser.

  之后几周打算再把《Runaway Bride(落跑新娘)》《My Best Friend’s Wedding(新娘不是我)》《Stepmom(真爱同心)》补上,都是好电影,不看就亏啦:D

比拼运气和抵抗力的时代

2009-5-3 22:22 | by 2ndboy

  这个五一假期过得很不爽,在 LP 家里这几天,目击她的一个亲戚在一件意外之后的一些境遇,感觉现在的国人普遍都比较暴虐,发生冲突之后首先想到的不是协商和坐下来谈谈,而是生理反应般的对骂,之后就是自然而然的对打,再然后是比谁住院的速度比较快,再然后是比谁的后台大。完全是一副冤冤相报不想了,欲把对方先踩死而后快的样子。

  对骂和对打这一块,自打有了善恶观念以来,看得已经太多了,就像某同事说的那样,中国早已不是书上自述的那个礼仪之邦了。你想连八荣八耻这种基本道德规范都要政府出面来宣传,这说明了什么问题?

  比住院,比验伤,甚至听说对方没什么问题都去住重症监护室啦!!!乖乖,重症监护室我有幸在探视病人的时候进去过一次,就 2 分钟不到,说 2 分钟太快?那不是医生赶我走地,是我实在受不了重症监护室的那种氛围自己出来的。说句不好听的,我没见过地狱长什么样,但我想应该不会比重症监护室差多少,里面充斥着各种垂死病患发出的稀奇古怪的声音和死亡气息。所以,受了点皮外伤的人居然能在重症监护室里睡一晚,就冲这点,我算服啦,全体投地那种!

  再说说比后台,比钱。某些名人曾经说过,现时的天朝其实是权贵资本主义,工作以后几年的体会告诉我,这不是一句假话。俗话说——有钱屌就大,特别是再加上有权。小老百姓们朝八晚六的过日子,看看歌舞升平的 news,也都挺和谐的,但那是没遇上跟权贵们发生冲突的时候,也就是说是你运气好。谁遇上谁就立马转会成为弱势群体,也就眼睛一闭一睁的功夫吧。

  从一个小村庄里发生的一件小事引出上面这么一堆文字来,似乎是多了点,估计跟我平时写的都是技术话题有关吧?!偶尔聊个别的话题就多喷一点儿。大部分都是牢骚话,但也都是平时看多了看不惯的东西之后的有感而发,希望我们生活的环境能更好一点吧。

使用 Artistic Style 对代码进行重新排版

2009-4-29 23:12 | by 2ndboy

  今天又捡起 AStyle 用了一下,是因为在看 Live555 的时候发现它的代码风格实在是跟我平时所习惯的大不相同,于是看代码之前都先用 AStyle 处理一下。这下,整个世界清静了!

  晚上干脆又看着 AStyle 的 document 整理出一份最适合自己的配置来,以后再看别人写的代码的时候方便一些,既然捡起来了,总要有所收获吧:)

最终命令行配置如下:
astyle.exe -A1 -T4 -S -w -m0 -p -D -v sample.cpp

各 option 简单解释如下:
-A1: 用 ANSI 预定义 style 来重新缩进,每个大括号单独一行
-T4: 强制使用 Tab 做缩进,一个 Tab 相当于 4 个空格
-S: 缩进 switch 中的 case 语句块
-w: 对多行宏定义做缩进
-m0: 设定多行条件表达式的最小缩进量为 0(不做缩进)
-p: 在操作符两端加空格
-D: 在函数调用的参数列表两端加空格
-v: 啰嗦模式

  为了在 VC 中使用方便,可以把 AStyle 设置成 Visual Studio IDE 的 tool(下面以 VC 6 为例说明):
1) 把 AStyle.exe 复制到 C:\Windows 下(PATH 设定中一般都包括系统目录,所以复制到这里,也方便把 AStyle 用到其它地方)
2) VC 菜单 Tools -> Customize… -> Tools Tab,新增一项“AStyle”
3) Command 中填“AStyle.exe”;Arguments 中填“-A1 -T4 -S -w -m0 -p -D -v $(FileName)$(FileExt)”;Initial Directory 不用填;选中“Use Output Window”(这样 AStyle 运行时的输出会被捕获到 VC 的 Output 窗口中,就不会出现一个控制台窗口了)
4) VC 菜单 Tools -> Options… -> Editor Tab,选中“Save Options”下面的“Save before running tools”(这样可以避免在没有存盘时使用 AStyle 导致未存盘的修改丢失)
Add AStyle for VC 6

设置好以后,想对当前打开的代码进行重新排版只需要点一下 Tools 菜单下的“AStyle”即可,非常方便!如果想再方便一点点,可以进入 Tools -> Customize… -> Commands Tab,然后把 Category 切换到 Tools,再把 AStyle 对应的图标拖到工具栏上去,以后想重排代码只需要点一下 Toolbar 上的按钮,少了一次点击:D

断点上传程序的一点心得

2009-4-25 21:42 | by 2ndboy

  最近两天花时间把之前做的基于文件的断点上传 Python 程序改成了基于数据块的断点上传,粒度变小之后,上传被打断后可以接着单个文件被打断的地方继续上传,而不用再传被打断之前就已经上传过的部分。心得记录如下:

1) 普通的文件上传用的是 STOR(STORE) 命令,断点上传需要用 APPE(APPEND) 命令:
两者的格式和参数一样,APPE 也没有用来指定 offset 的地方,所以 client 自己必须知道已经上传了多少字节,然后打开文件后把文件读指针设置到相应的地方再进行上传,Server 会把收到的数据附加到指定文件的后面。

2) 不能信赖 storbinary() 回调给出的上传数据 size:
由于上传期间随时可能断线,所以我在程序里维护了一个上传队列,里面记录了每个文件已经上传的 size,这个值是通过 storbinary() 的 callback 拿到的,示例代码如下:

  1. def UpdateSize( total_size, delta_size ):
  2.      total_size[0] = total_size[0] + delta_size
  3.  
  4.  ftp = ftplib.FTP( 'localhost' )
  5.  ftp.login( 'anonymous', '1@1.com' )
  6.  size = [0]
  7.  file = open( sys.path[0] + os.sep + 'test.txt', 'rb' )
  8.  ftp.storbinary( 'STOR /test.txt', file, 8192, lambda data: UpdateSize( size, len( data ) ) )
  9.  file.close()
  10.  print( size[0] )

上面这段示例代码有两个需要注意的地方:
a. lambda 里不能用赋值语句,所以要通过再去调用一个函数来达到计数的目的
b. Python 里 int 类型的变量不能传引用,内容一旦改变就会另建一个新的 int 变量,所以示例里用了一个 list 来保存上传的 size 而没有直接用一个 int 变量
(这里有更优雅的方法来达到同样的目的吗?路过的高人请留言:))

但是经过测试,用 storbinary() 的 callback 记录下来的已上传 size 在断线的情况下几乎都是不准确的,全部大于真正上传的数据 size。所以这个数值不是可信的,为了解决这个问题,我用了 ftplib 里的 FTP.size() 对这个值进行校正,效果不错。

顺便提一句,SIZE 命令不是标准所定义的,所以你在 RFC959 上是找不到这个命令的,但是大多数主流的 FTP Server 都支持这个命令,基本上可以放心使用。

3) 上传时处理文件要用双重 try-except:
《A Byte of Python》的 Chapter 13 当中在介绍不管有无异常发生,文件都能被关闭时用了如下一段代码:

  1. try:
  2.      f = file( 'test.txt' )
  3.      #... ...
  4.  finally:
  5.      f.close()

不管是不是由于 Python 2.x 跟 3.0 的语法差异导致的,总之这段代码在我的 Python 3 环境下是运行不了的,看来 try 跟 finally 块的变量作用域是不同的。倒是在《Dive Into Python》的 6.2.3 找到一段不错的实现:

  1. try:
  2.      fsock = open( filename, 'rb' )
  3.      try:
  4.          #... ...
  5.      finally:
  6.          fsock.close()
  7.  except:
  8.      pass

貌似最近有关技术的 post 很多,所以,没有输入就没有输出:D

50 State Quarters 齐了

2009-4-13 22:23 | by 2ndboy

  从去年三月份飞赴加州起,一直到上周五刚从 US 回来的同事帮我带回 08 年的最后 2 枚,我的 50 州 25 美分收集计划终于圆满结束。耗时将近一年又一个月:D

  目前我手里齐全的是一套 D 版(这里 D 版不是指盗版,指的是硬币反面有大写字母 D 的版本,D 表示 Denver——丹佛造币厂,P 表示 Philadelphia——费城造币厂),另外还有一套 P 版的差 3 枚(但是其中一些也是 D 版的:(),看来在西海岸收集 P 版的硬币是有些困难。

下面展示下 The United States Mint 50 State Quarters Program 的全景图(写程序从 Mint 网站上抓下来后拼合的):
50 State Quarters

  上周五得到的就是上图最后一排的最后 2 枚,分别是最晚加入美国的阿拉斯加(1959/1/3)和夏威夷(1959/8/21)。

  50 个 25 美分的币值是 $12.5,按今天的中行折算价 6.83 来算,一套 50 State Quarters 合人民币 ¥85.375,但是淘宝上居然有人卖 300 多,真不是一般黑啊!

Discuz! 6.1 用户密码的存放和生成方式

2009-4-12 13:00 | by 2ndboy

  自 Discuz! 6.1 以后,装 Discuz! 就必须安装 UCenter。经过分析,事实上自从跟 UCenter 集成以后,Discuz! 的用户密码就不是存放在 Discuz! 自己的 cdb_members 表里了,而是放在 UCenter 库里的 uc_members 表里。

  Discuz! 目录下的 register.php,其中向 cdb_members 表里插数据时密码的生成方式如下:

  1.  $password = md5( random( 10 ) );
  2.  ?>

只是用一个随机数的 MD5 值填充了用户密码字段,这跟 admin/members.inc.php 里的做法是一样的,根本没有使用用户自己输入的密码(可见即便 hacker 拿到了 Discuz! 库中用户表的数据,也没办法用 MD5 碰撞的方法找出一个可以登录的用户密码)。

  真正的用户密码保存在 UCenter 库的 uc_members 表里,代码在 uc_client/model/user.php 里:

  1.  function add_user($username, $password, $email, $uid = 0)
  2.  {
  3.      $salt = substr(uniqid(rand()), -6);
  4.      $password = md5(md5($password).$salt);
  5.      $sqladd = $uid ? 'uid=\''.intval($uid).'\',' : '';
  6.      //$appid = UC_APPID;
  7.      $this->db->query("INSERT INTO ".UC_DBTABLEPRE."members SET $sqladd username='$username', password='$password', email='$email', regip='".$this->base->onlineip."', regdate='".$this->base->time."', salt='$salt'");
  8.      $uid = $this->db->insert_id();
  9.      $this->db->query("INSERT INTO ".UC_DBTABLEPRE."memberfields SET uid='$uid'");
  10.      return $uid;
  11.  }
  12.  ?>

(UC_DBTABLEPRE 的定义是 define(’UC_DBTABLEPRE’, ‘`ucenter`.uc_’);)可以看出来,这里的密码生成算法跟生成 UCenter 创始人密码的算法是一样的,都是用一个加扰串结合原始密码的 MD5 再做一次 MD5。

  知道了以上原理,就可以手工对用户名密码做一些处理了。