开心网&豆瓣网发布状态PHP脚本(附Ping.fm的Custom URL使用方法)

2010-08-15 18:02 by hackerzhou

20100821最新更新:豆瓣/人人/BBS/开心/嘀咕/网易微博/新浪微博状态更新PHP代码终结版


后知后觉的前几天才开始用Ping.fm同时向多个平台同步消息,感觉很强大,比如可以同时给twitter,facebook,google talk等传送消息,而国内的新浪微博和嘀咕是可以通过gtalk机器人监听用户的gtalk签名更改然后自动发状态的,但是由于不支持国内的SNS社区,如开心和人人,一大排支持同步的站点一大半不认识,感觉有点别扭,好在Ping.fm支持Custom URL,可以自己写符合它标准的service接口就可以了,这样就可以实现让自定义的网站传送消息。

之前做过日月光华/复旦泉和人人网的发文脚本,使用的是php里的curl,这次修改了一下代码,紧凑了些。开心网的登录有些的复杂,因为几个月前开心网改变了原有的登录方式,不再明文发送密码,而是登录主页的时候在主页里返回一个密钥,然后输入的用户名和密码在提交的前经过一个极其猥琐的js脚本加密。因为我要实现自动登录并且发文,所以只能用php重写了一下原生的js脚本,由于是两种脚本语言,debug起来相对比较麻烦,而且我经常忘记在php的变量前面加上$符号,这个符号让我感觉很别扭。js有无符号右移的运算符>>>,php中只有有符号右移符>>,所以需要一个新的函数做无符号右移。

下面是用给定的密钥对密码进行加密的php代码(kaixin001encrypt.php),对应于js的加密脚本 http://s.kaixin001.com.cn/js/enlogin-7.js

<?php
function f($s, $x, $y, $z) {
		switch ($s) {
		case 0:
			return ($x & $y) ^ (~$x & $z);
		case 1:
			return $x ^ $y ^ $z;
		case 2:
			return ($x & $y) ^ ($x & $z) ^ ($y & $z);
		case 3:
			return $x ^ $y ^ $z;
		}
}

function rotl($x, $n) {
		return ($x << $n) | (shr32($x,32 - $n));
}

function tohs($str) {
		return dechex($str)."";
}

function getCharCode($str,$index){
	return ord(substr($str,$index));
}

function h($msg) {
		$K = array(0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6);
		$msg .= chr(0x80);

		$l = strlen($msg) / 4 + 2;
		$N = ceil($l / 16);
		$M;

		for ($i = 0; $i < $N; $i++) {
			for ($j = 0; $j < 16; $j++) {
				$M[$i][$j] = (getCharCode($msg,$i * 64 + $j * 4) << 24)
											| (getCharCode($msg,$i * 64 + $j * 4 + 1) << 16)
											| (getCharCode($msg,$i * 64 + $j * 4 + 2) <<  8 )
											| (getCharCode($msg,$i * 64 + $j * 4 + 3));
			}
		}
		$M[$N - 1][14] = ((strlen($msg) - 1) *  8 ) / pow(2, 32);
		$M[$N - 1][14] = floor($M[$N - 1][14]);
		$M[$N - 1][15] = ((strlen($msg) - 1) *  8 ) & 0xffffffff;
		$H0 = 0x67452301;
		$H1 = 0xefcdab89;
		$H2 = 0x98badcfe;
		$H3 = 0x10325476;
		$H4 = 0xc3d2e1f0;
		$W;
		$a;$b;$c;$d;$e;
		for ($i = 0; $i < $N; $i++) {
			for ($t = 0; $t < 16; $t++){
				$W[$t] = $M[$i][$t];
			}
			for ($t = 16; $t < 80; $t++){
				$W[$t] = rotl($W[$t - 3] ^ $W[$t - 8] ^ $W[$t - 14] ^ $W[$t - 16], 1);
			}
			$a = $H0;
			$b = $H1;
			$c = $H2;
			$d = $H3;
			$e = $H4;
			for ($t = 0; $t < 80; $t++) {
				$s = floor($t / 20);
				$T = (rotl($a, 5) + f($s, $b, $c, $d) + $e + $K[$s] + $W[$t]) & 0xffffffff;
				$e = $d;
				$d = $c;
				$c = rotl($b, 30);
				$b = $a;
				$a = $T;
			}
			$H0 = ($H0 + $a) & 0xffffffff;
			$H1 = ($H1 + $b) & 0xffffffff;
			$H2 = ($H2 + $c) & 0xffffffff;
			$H3 = ($H3 + $d) & 0xffffffff;
			$H4 = ($H4 + $e) & 0xffffffff;
		}
		return tohs($H0).tohs($H1).tohs($H2).tohs($H3).tohs($H4);
}

function en($p, $key) {
		if ($p == "") {
			return "";
		}
		$v = &sl($p, true);
		$k = &sl($key, false);
		$n = count($v) - 1;
		if (count($k) < 4) {
			$n = 3;
		}
		$z = $v[$n];
		$y = $v[0];
		$de = 2654435769;
		$mx;
		$e;
		$p;
		$q = floor(6 + 52 / ($n + 1));
		$sum = 0;

		while (0 < $q--) {
			$sum = $sum + $de & 0xffffffff;
			$e = shr32($sum,2) & 3;
			for ($p = 0; $p < $n; $p++) {
				$y = $v[$p + 1];
				$mx = (shr32($z,5) ^ $y << 2) + (shr32($y,3) ^ $z << 4) ^ ($sum ^ $y) + ($k[$p & 3 ^ $e] ^ $z);
				$z = $v[$p] = $v[$p] + $mx & 0xffffffff;
			}
			$y = $v[0];
			$mx = (shr32($z,5) ^ $y << 2) + (shr32($y,3) ^ $z << 4) ^ ($sum ^ $y) + ($k[$p & 3 ^ $e] ^ $z);
			$z = $v[$n] = $v[$n] + $mx & 0xffffffff;
		}
		return bh($v);
}

function bh(&$ar) {
		$charHex = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f');
		$str = "";
		$len = count($ar);
		for ($i = 0, $tmp = $len << 2; $i < $tmp; $i++) {
			$str .= $charHex[(($ar[$i >> 2] >> ((($i & 3) << 3) + 4)) & 0xF)];
			$str .= $charHex[(($ar[$i >> 2] >> (($i & 3) << 3)) & 0xF)];
		}
		return $str;
}

function &sl($s, $w) {
		$len = strlen($s);
		$v=array();
		for ($i = 0; $i < $len; $i += 4) {
			$v[$i >> 2] = getCharCode($s,$i) | getCharCode($s,$i+1) << 8 | getCharCode($s,$i+2) << 16 | getCharCode($s,$i+3) << 24;
		}
		if ($w) {
			$v[count($v)] = $len;
		}
		return $v;
}

function shr32($x, $bits){
	if($bits <= 0){
		return $x;
	}
	if($bits >= 32){
		return 0;
	}
	$bin = decbin($x);
	$l = strlen($bin);
	if($l > 32){
		$bin = substr($bin, $l - 32, 32);
	}elseif($l < 32){
		$bin = str_pad($bin, 32, '0', STR_PAD_LEFT);
	}
	return bindec(str_pad(substr($bin, 0, 32 - $bits), 32, '0', STR_PAD_LEFT));
}

function getPassword($oriPassword, $key) {
	return h(en($oriPassword, $key));
}
?>

登录开心网的时候需要先抓取一遍主页,然后获得密钥,然后call kaixin001encrypt.php中加密函数,获得密文之后把密钥和密文都发送到服务器进行验证。
豆瓣网会在登录成功后拿到一个hidden的input field,发状态和注销的时候都需要传这个参数。

发状态到豆瓣网,开心网,人人网,BBS的脚本如下:

<?php
$method=$_POST["method"];
$title=$_POST["title"];
$message=$_POST["message"];
$settings;
init($settings);
if($message!=""){
	send($method,$title,$message,$settings);
}

function init(&$settings){
	$settings=array(
		"enable_douban"=>"true",
		"douban_username"=>"##豆瓣用户名",
		"douban_password"=>"##豆瓣密码"),

		"enable_renren"=>"true",
		"renren_username"=>"##人人网用户名",
		"renren_password"=>"##人人网密码",

		"enable_bbs"=>"true",
		"bbs_username"=>"##BBS用户名",
		"bbs_password"=>"##BBS密码",
		"bbs_board_id"=>"##BBS版面ID",
		"bbs_night_api"=>"##0点到8点访问BBS的接口",
		"bbs_host"=>"http://bbs.fudan.sh.cn/q",

		"enable_kaixin001"=>"true",
		"kaixin001_username"=>"##开心网用户名",
		"kaixin001_password"=>"##开心网密码"
	);
}

function send($method,$title,$message,&$settings){
	if($settings["enable_douban"]==="true"){
		updateDouban($message,$settings["douban_username"],$settings["douban_password"]);
	}
	if($settings["enable_renren"]==="true"){
		updateRenren($message,$settings["renren_username"],$settings["renren_password"]);
	}
	if($settings["enable_bbs"]==="true"){
		if($method=="status"){
			$title="状态同步";
		}
		updateBBS($title,$message,$settings["bbs_board_id"],$settings["bbs_username"],$settings["bbs_password"],$settings["bbs_host"],$settings["bbs_night_api"]);
	}
	if($settings["enable_kaixin001"]==="true"){
		updateKaixin001($message,$settings["kaixin001_username"],$settings["kaixin001_password"]);
	}
}

function updateBBS($title,$content,$boardId,$bbs_username,$bbs_password,$bbs_host,$night_api) {
	$newtitle=iconv("UTF-8","GB2312",$title);
	$newcontent=iconv("UTF-8","GB2312",$content);
	$hour=intval(date("H"));
	if($hour==0||($hour>=16&&$hour<=23)){
		$bbs_host=$night_api;
	}
	$cookie_jar=tempnam('./tmp','bbs');
	$ch=&getCurl($cookie_jar,"POST",$bbs_host."/bbs/login");
	curl_setopt($ch,CURLOPT_POSTFIELDS,'id='.urlencode($bbs_username).'&pw='.urlencode($bbs_password));
	$str=&sendAndResult($ch);
	$ch=&getCurl($cookie_jar,"POST",$bbs_host."/bbs/snd?bid=".$boardId."&f=&e=0");
	curl_setopt($ch,CURLOPT_REFERER,$bbs_host.'/bbs/pst?bid='.$boardId);
	curl_setopt($ch,CURLOPT_POSTFIELDS,'title='.rawurlencode($newtitle).'&sig=3&text='.urlencode($newcontent));
	$str=&sendAndResult($ch);
	$str=iconv("GB2312","UTF-8",$str);
	if(strstr($str,"成功")!==false){
		echo "[BBS] succeed\n";
	}else{
		echo "[BBS] failed\n";
	}
	$ch=&getCurl($cookie_jar,"GET",$bbs_host."/bbs/logout");
	curl_setopt($ch,CURLOPT_REFERER,$bbs_host.'/bbs/doc?board=Test');
	$str=&sendAndResult($ch);
}

function updateRenren($status,$renren_username,$renren_password){
	$cookie_jar=tempnam('./tmp','renren');
	$ch=&getCurl($cookie_jar,"POST","http://passport.renren.com/PLogin.do");
	curl_setopt($ch,CURLOPT_POSTFIELDS,'email='.urlencode($renren_username).'&password='.urlencode($renren_password).'&autoLogin=true&origURL=http%3A%2F%2Fwww.renren.com%2FHome.do&domain=renren.com');
	$str=&sendAndResult($ch);
	$pattern="/get_check:'([^']+)'/";
	preg_match($pattern,$str,$matches);
	$get_check=$matches[1];
	$ch=&getCurl($cookie_jar,"POST","http://status.renren.com/doing/update.do");
	curl_setopt($ch,CURLOPT_POSTFIELDS,'c='.urlencode($status).'&raw='.urlencode($status).'&isAtHome=1&publisher_form_ticket='.$get_check.'&requestToken='.$get_check);
	curl_setopt($ch,CURLOPT_REFERER,'http://status.renren.com/ajaxproxy.htm');
	$ret=&sendAndResult($ch);
  if(strpos($ret,"{\"allMsg\":")===0){
		echo "[Renren] succeed\n";
	}else{
		echo "[Renren] failed\n";
	}
	//$ch=&getCurl($cookie_jar,"GET","http://www.renren.com/Logout.do?get_check=".$get_check);
	//$ret=&sendAndResult($ch);
}

function updateKaixin001($status,$kaixin001_username,$kaixin001_password){
	include("kaixin001encrypt.php");
	$cookie_jar=tempnam('./tmp','kaixin001');
	$ch=&getCurl($cookie_jar,"GET","http://www.kaixin001.com/");
	$str=&sendAndResult($ch);
	$pattern="/new EnLogin\('([^']+)'/";
	preg_match($pattern,$str,$matches);
	$encryptKey=$matches[1];
	$encryptResult=getPassword($kaixin001_password,$encryptKey);
	$ch=&getCurl($cookie_jar,"POST","http://www.kaixin001.com/login/login_api.php");
	curl_setopt($ch,CURLOPT_POSTFIELDS,'ver=1&email='.urlencode($kaixin001_username).'&rpasswd='.$encryptResult.'&encypt='.$encryptKey.'&url=%2Fhome%2F&remember=1');
	$str=&sendAndResult($ch);
	$ch=&getCurl($cookie_jar,"POST","http://www.kaixin001.com/friend/status_submit.php");
	curl_setopt($ch,CURLOPT_POSTFIELDS,'state='.urlencode($status));
	curl_setopt($ch,CURLOPT_REFERER,'http://www.kaixin001.com/home/');
	$ret=&sendAndResult($ch);
	if(strpos($ret,"{\"state\":")===0){
		echo "[Kaixin001] succeed\n";
	}else{
		echo "[Kaixin001] failed\n";
	}
	//$ch=&getCurl($cookie_jar,"GET","http://www.kaixin001.com/login/logout.php");
	//$ret=&sendAndResult($ch);
}

function updateDouban($status,$douban_username,$douban_password){
	$cookie_jar=tempnam('./tmp','douban');
	$ch=&getCurl($cookie_jar,"POST","http://www.douban.com/accounts/login");
	curl_setopt($ch,CURLOPT_POSTFIELDS,'form_email='.urlencode($douban_username).'&form_password='.urlencode($douban_password).'&remember=on&user_login=%E8%BF%9B%E5%85%A5');
	$str=&sendAndResult($ch);
	$pattern="/<input type=\"hidden\" name=\"ck\" value=\"([^\"]+)\"/";
	$isLogin=preg_match($pattern,$str,$matches);
	$ck=$matches[1];
	$ch=&getCurl($cookie_jar,"POST","http://www.douban.com/");
	curl_setopt($ch,CURLOPT_POSTFIELDS,'ck='.$ck.'&mb_text='.urlencode($status));
	curl_setopt($ch,CURLOPT_REFERER,'http://www.douban.com/');
	$ret=&sendAndResult($ch);
  if($isLogin){
		echo "[Douban] succeed\n";
	}else{
		echo "[Douban] failed\n";
	}
	//$ch=&getCurl($cookie_jar,"GET","http://www.douban.com/accounts/logout?ck=".$ck);
	//$ret=&sendAndResult($ch);
}

function &getCurl($jar,$method,$url){
	$ch=curl_init();
	curl_setopt($ch,CURLOPT_COOKIEJAR,$jar);
	curl_setopt($ch,CURLOPT_COOKIEFILE,$jar);
	curl_setopt($ch,CURLOPT_URL,$url);
	curl_setopt($ch,CURLOPT_FOLLOWLOCATION,TRUE);
	curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);
	if($method=="POST"){
		curl_setopt($ch,CURLOPT_POST,TRUE);
	}else{
		curl_setopt($ch,CURLOPT_POST,FALSE);
	}
	//curl_setopt($ch,CURLOPT_USERAGENT,'Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6');
	return $ch;
}

function &sendAndResult(&$ch){
	$str=curl_exec($ch);
	curl_close($ch);
	unset($ch);
	return $str;
}
?>
本文基于 署名 2.5 中国大陆 许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名 hackerzhou 并包含 原文链接
发表评论

本文有 28 条评论

  1. hackerzhou
    2010-09-13 18:22


    biechao:

    自己的问题……,找到原因了

    呵呵,找到原因就好,其实原理大同小异的

  2. biechao
    2010-09-13 18:21

    自己的问题……,找到原因了

  3. biechao
    2010-09-13 17:22

    您好,我在请问一下,为什么登陆后访问个人主页可以,但是访问某些页面的时候,即便把地址都改了,可是输出还是之前的页面的内容。例如
    http://wap.kaixin001.com/friend/?uid=79793060&verify=79793060_79793060_1284369337_37aead8a4dc5b50804d6335cb19aa017_kx 这个页面应该输出的是好友列表,可是输出结果还是首页http://wap.kaixin001.com/home/?uid=79793060&verify=79793060_79793060_1284369665_ff8632347629afe76ee9797a73a2fe2b_kx
    的内容

  4. hackerzhou
    2010-09-13 16:11


    biechao:

    恩,我知道了,已经修改好了,可以改$_POST
    的参数,呵呵,其似我不需要发信息的,只要能登陆后访问页面就可以了,这样就可以把页面抓下来,谢谢,非常感谢,呵呵,看来我得用这个代码去登陆了,用那个万一开心网稍微改一下我就蒙了,不知道这个会不会经常改

    应该不会经常改,wap方式应该变动不会很大
    就算有变动,微调一下就好了

  5. biechao
    2010-09-13 16:10

    恩,我知道了,已经修改好了,可以改$_POST
    的参数,呵呵,其似我不需要发信息的,只要能登陆后访问页面就可以了,这样就可以把页面抓下来,谢谢,非常感谢,呵呵,看来我得用这个代码去登陆了,用那个万一开心网稍微改一下我就蒙了,不知道这个会不会经常改

  6. hackerzhou
    2010-09-13 16:04


    biechao:

    您好,你发的 这个code我之前看过,因为用的时候有问题,您这个代码是直接可以修改需要的服务后单独运行还是需要另外一个页面传参数过来,因为我直接运行,有问题,自己修改了一些后,怎么都运行 不正确

    不能作为服务运行,是作为ping.fm的custom url的功能使用
    要是不明白如何修改的话,建议去补一下php的基础,那段code是需要接受外部POST的参数的

  7. biechao
    2010-09-13 15:52

    您好,你发的 这个code我之前看过,因为用的时候有问题,您这个代码是直接可以修改需要的服务后单独运行还是需要另外一个页面传参数过来,因为我直接运行,有问题,自己修改了一些后,怎么都运行 不正确

  8. hackerzhou
    2010-09-13 15:39


    biechao:

    开心网是不是又在登陆地方做手脚了,怎么之前可以登录的代码,现在登陆不上去了,是修改了加密文件了吗,还是怎么回事

    见我最新的code https://www.hackerzhou.me/2010/08/douban-renren-bbs-kaixin001-digu-163-sina-microblog-status-update-final-version.html ,这上面用的是wap方式登录开心网,就不会有问题了

发表评论