<?php
// session_start();
date_default_timezone_set("Asia/Shanghai");
class MouseHole{
public $default_host = 'https://www.google.com';
public $param_name = 'url';
public $param_special = 'site';
public $param_static = 'static_res';
public $white_list = [
'url','site'
];
public $gbk_site = [
'www.mitbbs.com'
];
//特殊站点替换无效链接
public $cdn_list = [
'www.archiveofourown.org'=>[
'//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js'=>'//cdn.bootcss.com/jquery/1.9.0/jquery.min.js',
'//ajax.googleapis.com/ajax/libs/jqueryui/1.10.0/jquery-ui.min.js'=>'//cdn.bootcss.com/jqueryui/1.10.0/jquery-ui.min.js'
]
];
public $content_type = [
'png'=>'image/png',
'gif'=>'image/gif',
'webp'=>'image/webp',
'jpg'=>'image/jpeg',
'ico'=>'image/x-icon',
'css'=>'text/css',
'json'=>'application/json',
'js'=>'application/javascript',
'woff'=>'font/woff',
];
function __construct(){
$this->HTTP_ACCEPT_LANGUAGE = empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])?'':$_SERVER['HTTP_ACCEPT_LANGUAGE'];
$this->HTTP_USER_AGENT = empty($_SERVER['HTTP_USER_AGENT'])?'':$_SERVER['HTTP_USER_AGENT'];
$this->get = $_GET;
$this->post = $_POST;
$this->self_name = $_SERVER['PHP_SELF'];
}
//主进程
function execute(){
if(empty($this->get) && empty($this->post)){
//主页
return $this->index();
}else if(!empty($this->get[$this->param_special])){
//测试
return $this->catchRes();
}else if(!empty($this->get[$this->param_static])){
//静态文件
return $this->staticSource();
}else if(!empty($this->get['self_code'])){
//源码
show_source(__FILE__);
}else{
//解析页面
return $this->genHtml();
}
}
//error
function showError($errorMessage, $data = []){
echo 'errorMessage:' . $errorMessage . "<br />errorData:";
print_r($data);
exit;
}
//简易主页
function index(){
$ret = '';
//展示常用网站
$showList = array(
);
$showList2 = [
];
foreach($showList as $item){
$ret .= sprintf("<a href='?%s=%s'>%s</a><br><br>",
$this->param_name, $item, $item);
}
foreach($showList2 as $item){
$ret .= sprintf("<a href='%s'>%s</a><br><br>",
$item, $item);
}
$ret .= sprintf("<form action='%s'><input name='%s' type='search' value='%s' /> <input type='submit' value='打开'></form>",
$this->self_name, $this->param_name, $this->default_host);
$ret .= "<a href='?self_code=1'>查看源代码</a><br><br>";
return $ret;
}
//验证host正确性
function checkRequest(){
/*
- check:get post 中至少有一个有host
- 获得url
*/
$url = '';
if(!empty($this->get[$this->param_name])){
$url = $this->get[$this->param_name];
}else if(!empty($this->post[$this->param_name])){
$url = $this->post[$this->param_name];
}
$url = urldecode($url);
//检查url
if(preg_match("/^(https?:\/\/)?[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]+)+(:\d+)?(\/.*)?$/", $url)){
if(!preg_match("/^https?:\/\//", $url)){
$url = 'https://' . $url;
}
}else{
$this->showError('url不合法', array($url));
}
$paramStr = '';
//拼接额外get参数
foreach($this->get as $k => $v){
if(in_array($k,$this->white_list)){
continue;
}
$paramStr .= '&' . $k . '=' . urlencode($v);
}
if(strpos($url, '?') > 0){
$url .= $paramStr;
}else{
$url .= '?' . substr($paramStr,1);
}
preg_match_all("/^(https?:\/\/)([a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]+)+(:\d+)?)/", $url, $res);
$this->requestProtocol = $res[1][0];
$this->requestSite = $res[2][0];
if(isset($this->cdn_list[$this->requestSite])){
$this->IS_site_special = true;
}else{
$this->IS_site_special = false;
}
return $url;
}
function getSite(){
return $this->requestProtocol . $this->requestSite;
}
//修复静态文件链接
function staticUrl(&$html){
$arr_find = array();
$arr_replace = array();
preg_match_all('/<script.*?src="([^"]+)".*?><\/script>/', $html, $scriptList);
preg_match_all('/<link.*?href="([^"]+)".*?>/', $html, $scriptList2);
foreach(array_merge($scriptList[1], $scriptList2[1]) as $k => $scriptUrl){
/*
2. 非本站完整链接,特殊站点,替换
3. 本站完整链接,过洞
4. 本站相对路径链接,补充过洞
*/
$scriptUrl = trim($scriptUrl);
//是否为完整链接
if(preg_match("/^(https?|\/\/)/", $scriptUrl)){
//检查静态文件的域名是否跟网站一致
//如果一致,依然不能跳过
if(strpos($scriptUrl, $this->getSite()) === 0){
//与站点一致,取路径部分,待替换
$scriptUrl = substr($scriptUrl, strlen($this->getSite()));
}else{
//是否有cdn地址
if($this->IS_site_special && isset($this->cdn_list[$this->requestSite][$scriptUrl])){
//替换
$arr_find[] = $scriptUrl;
$arr_replace[] = $this->cdn_list[$this->requestSite][$scriptUrl];
}
continue;
}
}
if($scriptUrl == '/'){
continue;
}
if(preg_match("/^\/\?/", $scriptUrl)){
continue;
}
//替换
$arr_find[] = $scriptUrl;
$arr_replace[] = $this->self_name . '?' . $this->param_static . '=' . urlencode($this->getSite() . $scriptUrl);
}
// var_dump($arr_find,$arr_replace);
// exit();
$html = str_replace($arr_find, $arr_replace, $html);
}
//获取html页面
function genHtml(){
//检查链接
$this->requestUrl = $this->checkRequest();
//设置编码
if(in_array($this->requestSite, $this->gbk_site)){
header('Content-Type:text/html;charset=GBK');
}else{
header('Content-Type:text/html;charset=UTF-8');
}
//请求html
$html = $this->holeGet($this->requestUrl, $this->post);
//修复script link标签地址
$this->staticUrl($html);
//拼接footjs用于修复页面中的links form表单
return $html . $this->footJs();
}
//debug
function catchRes(){
// $this->IS_site_special = true;
$html = file_get_contents($this->get[$this->param_special]);
//$this->staticUrl($html);
return $html;
}
//输出静态文件
function staticSource(){
$url = strtolower($this->get[$this->param_static]);
$mReg = implode('|', array_keys($this->content_type));
$matchStr = "/\.(".$mReg.")/";
preg_match_all($matchStr, $url, $res);
//var_dump($res);exit;
header("Content-Type: " . $this->content_type[$res[1][0]]);
header('Cache-Control:max-age=315360000');
return file_get_contents($this->get[$this->param_static]);
}
function holeGet($url, $post_data=array())
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, false);
$header = array(
'Accept-Language: ' . $this->HTTP_ACCEPT_LANGUAGE,
'User-agent: ' . $this->HTTP_USER_AGENT,
); //设置一个你的浏览器agent的header
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_USERAGENT, $this->HTTP_USER_AGENT);
//跟随302跳转
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
if(!empty($post_data)){
//设置post方式提交
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);
}
$res = curl_exec($ch);
// $rescode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return $res;
}
function footJs(){
$site = $this->getSite();
return
<<<EOF
<script>
//https://www.baidu.com:80
//window.onload = function(){
var Req_Site = "$site";
var Main_Site = window.location.protocol + '//' + window.location.host;
var Main_Url = window.location.href.match(/[^?]*/)[0];
Main_Url = Main_Site + '$this->self_name';
var i,o;
var Req_Url,Str_Param;
var Reg_Path = /^(https?:)?\/\/[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]+)+(\/.*)/;
var Reg_Param = /(\?|&)([^&\?=]+=[^&\?=]+)/g;
var Reg_Filename = /\/[^\?]*/;
//console.log(document.links);
//将a链接中省略域名的链接修复
for(i = 0; i < document.links.length; i++){
//console.log('scan url:' + document.links[i].href);
//排除空链接
if(document.links[i].href.match(/^javascript:/i)){
//console.log('empty');
continue;
}
//排除非当前站的链接(针对google还有导航站)
if(document.links[i].href.search(Req_Site) == -1 && document.links[i].href.search(Main_Site) == -1){
//console.log('not main');
continue;
}
//获取路径部分
Req_Path = document.links[i].href.match(Reg_Path)[3];
//console.log('i='+i,Req_Path);
//拆分路径中的路径和参数
Req_Filename = Req_Path.match(Reg_Filename)[0];
Req_Param = Req_Path.match(Reg_Param);
Str_Param = '';
if(Req_Param != null){
for(o=0;o<Req_Param.length;o++){
if(Req_Param[o].substr(1,3)=='url')continue;
Str_Param += '&' + Req_Param[o].substr(1);
}
}
//console.log(Req_Path, Str_Param);
//链接路径部分和域名部分
Req_Url = encodeURIComponent(Req_Site + Req_Filename);
//console.log('Req_Url:' + Req_Url);
//拼接上本页面链接
document.links[i].href = Main_Url +'?url=' + Req_Url + '&' + Str_Param.substr(1);
}
//将图片链接修复
for(i = 0; i < document.images.length; i++){
console.log('scan url:' + document.images[i].src);
//排除空链接
if(document.images[i].src.match(/^#/i)){
//console.log('empty');
continue;
}
//排除非当前站的链接
if(document.images[i].src.search(Req_Site) == -1 && document.images[i].src.search(Main_Site) == -1){
//console.log('not main');
continue;
}
//获取路径部分
Req_Path = document.images[i].src.match(Reg_Path)[3];
//console.log('i='+i,Req_Path);
//拼接上本页面链接
document.images[i].src = Main_Url +'?static_res=' + Req_Site + Req_Path;
if (document.images[i].srcset != '') {
document.images[i].srcset = '';
}
}
for(i = 0; i < document.forms.length; i++){
//排除跳转到其他站的表单
if(document.forms[i].action.search(Req_Site) == -1 && document.forms[i].action.search(Main_Site) == -1){
continue;
}
Req_Path = document.forms[i].action.match(Reg_Path)[3];
Req_Url = encodeURIComponent(Req_Site + Req_Path);
document.forms[i].action = Main_Url +'?url=' + Req_Url;
document.forms[i].innerHTML += '<input type="hidden" name="url" value="' + Req_Url + '" />';
}
console.log('url:','$this->requestUrl');
//};
</script>
EOF;
}
}
//echo json_encode($_SERVER);exit;
$obj = new MouseHole();
echo $obj->execute();
// var_dump($obj);
exit;