分類
程序

PHP 使用 Nominatim 的逆地理編碼

之前一直使用谷歌的逆地理編碼服務,多年來運行良好。但是谷歌畢竟是一家巨大的商業公司,所以我現在改用 Nominatim

Nominatim(來自拉丁語,“按名稱”)是一個通過名稱和地址搜索開放街圖數據,根據開放街圖點來生成合成地址(逆地理編碼)的工具。
<?php
header("Access-Control-Allow-Origin: *");
header('Content-type: text/json');
header("Cache-Control: no-cache, must-revalidate");

$lat=$_POST["lat"];
$lng=$_POST["lng"];
$lang=$_POST["lang"];

$url = "https://nominatim.openstreetmap.org/reverse?format=geojson&lat=".$lat."&lon=".$lng."&accept-language=".$lang;

$ch = curl_init(); 
//set your own agent name
$agent = 'location/0.2(ft.shaman.eu.org)';
curl_setopt ($ch, CURLOPT_URL, $url); 
curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1); 
curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT,20); 
curl_setopt($ch, CURLOPT_USERAGENT, $agent);
$content = curl_exec($ch); 
curl_close($ch);

$content = json_decode($content);
$resultArray = array();
foreach($content->features[0]->properties->address as $key => $value) {
    //remove country_code and postcode
    if ($key=='country_code' or $key=='postcode'){            
    }else{
        array_push($resultArray,$value);
    }
}
echo implode(",",$resultArray);
?>	
curl -d 'lat=22.57776&lng=113.94849&lang=zh-TW' https://your.api.domain/api.php
西丽华昌大厦,西丽南路,松坪村,南山区,西丽街道,广东省,中国
分類
Linux

服務器連接pptp

linux pptp client managment

需要從某網站頻繁獲取數據,但目標網站對IP限制嚴格,於是想到用vpn。市面上(淘寶)有種每次連接都會自動切換IP的PPTP VPN,很符合需求,下面就是怎麼用了,環境是CentOS6。

#安裝pptp
yum install pptp pptp-setup
#配置pptp
pptpsetup --create v --server vpn.server.address --username USERNAME --password PASSWORD
#淘寶的這種pptp vpn一般是不用加密設置的,萬一需要,可以在配置命令最後加--encrypt參數
#為了方便後續使用,我們複製兩個命令
cp /usr/share/doc/ppp-2.4.5/scripts/pon /usr/sbin/
cp /usr/share/doc/ppp-2.4.5/scripts/poff /usr/sbin/
chmod +x /usr/sbin/pon
chmod +x /usr/sbin/poff
#啟動pptp
pon v
#如果啟動成功路由表中應該會出現一個ppp0的設備
route -n
#如果沒出現可以查看那裡出了問題
tail -n 10 /var/log/messages | grep ppp
#這是如果把網關設置到ppp0所有流量就都走vpn了\
#但是啊,你將失去ssh連接,很恐怖吧\
#這時最好有服務器的網頁端供你連到內網進行操作\
#當然,重啟服務器也能恢復連接
#下面設置路由表
/sbin/route del -net default
/sbin/route add -net default dev ppp0
#此時所有流量就pptp了,通過下面命令查看
curl http://myip.dnsdynamic.org/
#執行需要pptp的ip完成的任務
#關閉vpn
poff
#理論上此時應該恢復之前的路由表
#但我偷了個懶,重啟了一下網絡
/etc/init.d/network restart
#從pon到重啟網絡,就完成了一個工作循環。

使用php腳本完成pptp的切換,其實就是php調用bash,兩點:運行用戶為root;命令最好寫相對路徑。

#!/usr/bin/php
<?php
class HttpClient{
    private $ch;

    function __construct($cookie_jar){
        $this->ch = curl_init();
        curl_setopt($this->ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0');
        curl_setopt($this->ch, CURLOPT_TIMEOUT, 40);
        curl_setopt($this->ch, CURLOPT_FOLLOWLOCATION, TRUE);
        curl_setopt($this->ch, CURLOPT_AUTOREFERER, true);
        curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, TRUE);
        curl_setopt($this->ch, CURLOPT_COOKIEJAR, $cookie_jar);
        curl_setopt($this->ch, CURLOPT_COOKIEFILE, $cookie_jar);
    }

    function __destruct(){
        curl_close($this->ch);
    }

    final public function setReferer($ref=''){
        if($ref != ''){
            curl_setopt($this->ch, CURLOPT_REFERER, $ref);
        }
    }

    final public function Get($url, $header=false, $nobody=false){
        curl_setopt($this->ch, CURLOPT_POST, false);
        curl_setopt($this->ch, CURLOPT_URL, $url);
        curl_setopt($this->ch, CURLOPT_HEADER, $header);
        curl_setopt($this->ch, CURLOPT_NOBODY, $nobody);
        return curl_exec($this->ch);
    }

    final public function Post($url, $data=array(), $header=false, $nobody=false){
        curl_setopt($this->ch, CURLOPT_URL, $url);
        curl_setopt($this->ch, CURLOPT_HEADER, $header);
        curl_setopt($this->ch, CURLOPT_NOBODY, $nobody);
        curl_setopt($this->ch, CURLOPT_POST, true);
        curl_setopt($this->ch, CURLOPT_POSTFIELDS, http_build_query($data));
        return curl_exec($this->ch);
    }
}

function getNewIP(){
    $logIPFile="/home/logIP.txt";
    $res=array();
    exec("/usr/sbin/pon v", $res);
    sleep(3);
    exec("/sbin/route del -net default", $res);
    sleep(2);
    exec("/sbin/route", $res);
    sleep(6);
    exec("/sbin/route add -net default dev ppp0", $res);
//	var_dump($res);
    sleep(1);
//    需要一個cookie文件,創建一個空文件即可
    $http = new HttpClient('/home/dump.txt');
    $ip = $http->Get("http://myip.dnsdynamic.org/");
    sleep(1);
    file_put_contents($logIPFile, date("Y-m-d H:i:s", time())."_".$ip.PHP_EOL , FILE_APPEND | LOCK_EX);

    $ips=array();
    exec("tail -n 2 '$logIPFile'", $ips);
    $isNewIP=0;
    foreach( $res as $oldIP ){
        if(strpos($oldIP, $ip) != false){
           $isNewIP=$isNewIP+1;
       }
    }

    return $isNewIP; 
}

function restoreIP(){
    exec("/usr/sbin/poff",$res);
//	var_dump($res);
    sleep(2);
    exec("/etc/init.d/network restart");
}

//如果IP沒有重複兩次以上就執行任務
if( getNewIP($logIPFile) <2 ){
//    doYourOwnStaff();
} 
restoreIP();
exit();
?>

crontab的設置,這裡不用crontab -e,而是直接編輯/etc/crontab:

nano /etc/crontab
#指定運行命令的用戶為root
03 * * * * root /path/to/the/script

這樣就完工了,感謝CentOS 6下配置PPTP VPN客户端在 Linux 命令列進行 PPTP VPN 連線。在完全不了解linux路由表和iptables的情況下,我也只能做到如此了。其實理想狀態是只轉發http和https到pptp,有空再研究吧,新年快樂!


由於政策原因,動態pptp沒那麼好買了。於是年後換了一批靜態pptp,做法就是新建多個pptp配置,然後隨機取。

$vpns = array("v47", "v11", "v18", "v12");
$rand_keys = array_rand($vpns, 2);
$vpn = $vpns[$rand_keys[0]] ;

在設置過程中,遇到了unknown authentication type 26; Naking錯誤,解決辦去掉/etc/ppp/options.pptp文件中的require-mppe-128的注釋,並在修改配置文件為如下:

# written by pptpsetup
pty "pptp SERVER --nolaunchpppd"
lock
noauth
refuse-pap
refuse-eap
refuse-chap
refuse-mschap

nobsdcomp
nodeflate
require-mppe-128
name MYUSERNAME
remotename CONFNAME
ipparam CONFNAME

另一種可能的錯誤是連接的時候出現LCP: timeout sending Config-Requests,我是通過chkconfig iptables off關閉iptables解決的,反正我在內網。

上文中的/etc/init.d/network restart有時並不能恢復本機網絡,此時我們可以替換腳本中的這一句為

#網關改回原來的網關
subprocess.call('/sbin/route add default gw 172.16.2.1 netmask 0.0.0.0 dev eth0', shell=True)

linux l2tp client managment

時間來到2018年,pptp換成了l2tp,發現同樣的需求使用NetworkManager真是簡單。設置好l2tp連接後,一行命令即可打開關閉連接。

#passwd-file只需配置一行PSK
vpn.secrets.password:YOUR_PSK
#打開l2tp連接
subprocess.call('nmcli con up l2tp76 passwd-file /home/42/vpnpass.txt', shell=True)
#關閉l2tp連接
subprocess.call('nmcli con down l2tp76', shell=True)

本文更新於 2018/11/09。

分類
网站

php生成圖片與crontab

PHP生成圖片

//@function.php
//先獲取個GET參數等下寫圖片里
if ($_GET["name"]) {
    $name = htmlspecialchars($_GET["name"]);
} else {
    $name = '';
}
//創建圖像
$img=getImgType($img_path);
$img=createImg($img,$wordsPhoto,$left_px,$top_px,25,0,20,'19f','ARIALUNI');
//保存圖像
$img_info=saveImg($img,'png');

//創建圖像函數
function getImgType($img_path) {
	$img = getimagesize ( $img_path );
	switch ($img [2]) {
		case 1 :
			$img = @imagecreatefromgif ( $img_path );
			break;
		case 2 :
			$img = @imagecreatefromjpeg ( $img_path );
			break;
		case 3 :
			$img = @imagecreatefrompng ( $img_path );
			break;
		default :
			$img = @imagecreatefrompng ( $img_path );
	}
	return $img;
}
//修改圖像函數
function createImg($img, $str, $x, $y, $length = 20, $angle = 0, $size = 12, $color = '39f', $font = 'kanghua') {
	switch ($color) {
		case '19f' :
			$color = imagecolorallocate ( $img, 22, 157, 252 );
			break;
		case 'f37' :
			$color = imagecolorallocate ( $img, 255, 51, 119 );
			break;
		case '63a' :
			$color = imagecolorallocate ( $img, 68, 172, 106 );
			break;
		case 'f90' :
			$color = imagecolorallocate ( $img, 255, 158, 3 );
			break;
		case 'a60' :
			$color = imagecolorallocate ( $img, 172, 106, 0 );
			break;
		case '790' :
			$color = imagecolorallocate ( $img, 113, 149, 13 );
			break;
		case 'fff' :
			$color = imagecolorallocate ( $img, 255, 255, 255 );
			break;
		case '000' :
			$color = imagecolorallocate ( $img, 0, 0, 0 );
			break;
		default :
			$color = imagecolorallocate ( $img, 67, 157, 252 );
	}
	switch ($font) {
		case 'ARIALUNI' :
			$font = './../font/ARIALUNI.ttf';
			break;
		case 'CODE2000' :
			$font = './../font/CODE2000.ttf';
			break;
		case 'SarunsManorah' :
			$font = './../font/SarunsManorah.ttf';
			break;
		case 'FreeSerif' :
			$font = './../font/FreeSerif.ttf';
			break;
		case 'kanghua' :
			$font = './../font/kanghua.ttf';
			break;
		case 'shishang' :
			$font = './../font/shishang.ttf';
			break;
		case 'yahei' :
			$font = './../font/yahei.ttf';
			break;
		default :
			$font = './../a_include/font/kanghua.ttf';
	}
	$str = wordwrap_utf8 ( $str, $length );
	imagettftext ( $img, $size, $angle, $x, $y, $color, $font, $str );
	return $img;
}
//文字换行
function wordwrap_utf8($string, $length = 20, $break = "\n", $cut = false) {
	if ($length == 0) {
		return $string;
	}
	preg_match_all ( '/./u', $string, $matches );
	$s = $matches [0];
	$ct = count ( $s );
	for($i = 0; $i < ceil ( $ct / $length ); $i ++) {
		$ns .= implode ( '', array_slice ( $s, $i * $length, $length ) ) . $break;
	}
	return $ns;
}
function saveImg($img, $type = 'png') {
	$img_url = '';
	$img_name = time ().rand(10,99);
        //php5.5以上才支持webp
	if ($type == 'jpg') {
		$img_url = '/a_cache/' . $img_name . '.jpg';
		$img_filename = dirname ( dirname ( dirname ( __FILE__ ) ) ) . '/a_cache/' . $img_name . '.jpg';
		imagejpeg ( $img, $img_filename );
	} else {
		$img_url = '/a_cache/' . $img_name . '.png';
		$img_filename = dirname ( dirname ( dirname ( __FILE__ ) ) ) . '/a_cache/' . $img_name . '.png';
		imagepng ( $img, $img_filename );
	}
	imagedestroy ( $img );
	$img_info ['img_name'] = $img_name;
	$img_info ['img_url'] = '..' . $img_url;
        return $img_info;
}

網頁展示圖片

//@show.php
require_once 'function.php';

<div style="width:100%;text-align:center;background-color: #58C7C2;padding: 0.52em 0.1em 0.52em 0.1em;">
<img id="res_pic" style="display: block;width: 70%;margin-left: 15%;" src="<?php echo $img_info['img_url'];?>" />
</div>

用corntab定期清理緩存圖片

創建可執行文件/www/del_cache,內容如下,意思是刪除兩天前的文件

#!/bin/bash 
find /path/to/your/a_cache/* -mtime +2 -exec rm -f {} \;

把執行文件加入定時任務

crontab -e
#如果有多個編輯器,可能會讓選擇編輯器,但我CentOS中有nano,並沒有提示,我還是得用vi

修改並把下面一行粘貼到最後,意思是每小時的0分執行一次

00 * * * * /path/to/the/script

有時我們需要每30秒執行一次定時任務,但是crontab只能精確到分,可以這麼做:

* * * * * /path/to/executable param1 param2
* * * * * ( sleep 30 ; /path/to/executable param1 param2 )

這裡還有一個每90秒的例子:

*/3 * * * * /path/to/executable param1 param2
*/3 * * * * ( sleep 90 ; /path/to/executable param1 param2 )

有時我們需要以root用戶來執行crontab中的命令,僅僅切換到root用戶然後使用crontab -e似乎還是不行,後來發現(CentOS下)可以這樣:

nano /etc/crontab
#在這裡編輯需要定時執行的命令,並指定運行命令的用戶
03 * * * * root /path/to/the/script

有時的需求是這樣的,定時任務執行時間不確定,比如有時10分鐘,有時30分鐘,但是我們還不想同時執行兩個任務,這時可以使用flock文件鎖來控制只有一個程序在跑。

#下面的效果就是,每5分鐘檢查一次,如果在ping就跳過此次執行
*/5 * * * * flock -xn /tmp/42ping.lock -c 'ping -c 1000 ft.wupo.info >> ping.log'
#測試發現即使程序異常中斷,lock也會被解除,下次任務仍然得以運行

參考了How to Delete Old Files In A Folder Automatically In LinuxRunning a cron every 30 seconds。對於crontab的寫法,可以到crontab.guru來驗證是否正確。其實我主要是想記錄這一段。

本文更新於 2017/03/07。

分類
程序

IP地址和歸屬地

移動2G網速好爛!看個IP歸屬地都要等好久。於是自己寫了個小小的php,輸出用不了1K數據,這樣既省流量又快速。代碼在Github:根據IP查看地理位置


//服務端獲取客戶端IP
$ip = getenv('HTTP_CLIENT_IP')?:
    getenv('HTTP_X_FORWARDED_FOR')?:
    getenv('HTTP_X_FORWARDED')?:
    getenv('HTTP_FORWARDED_FOR')?:
    getenv('HTTP_FORWARDED')?:
    getenv('REMOTE_ADDR');

本文更新於 2016/09/19。

分類
程序

在线小六壬

公曆:

如果輸入框不支持時間選擇,請以2015-05-19T10:36格式輸入查詢。

本文更新於 2021/05/04。

分類
程序 网站

自动检查网页更新

需求:http://www.cnca.gov.cn/ywzl/gjgnhz/jkzl/这个网页会公布进口水产品境外生产企业注册名单,但公布日期不一定,所以想自动检查美国(2014年07月21日)这个条目是否更新了,如果更新了发邮件通知我。方法:获取网页,查看网页是否存在“美国(2014年07月21日)”若不存在则更新了。发邮件用PHPMailer的SMTP发送邮件很方便。

check.php

<?php
ignore_user_abort();//关掉浏览器,PHP脚本也可以继续执行.
set_time_limit(0);//通过set_time_limit(0)可以让程序无限制的执行下去
$interval=60*60*10;//单位是秒,每10小时执行一次
do{
	$run = include 'config.php';
	if(!$run) die('process abort');
	
	$url = "http://www.cnca.gov.cn/ywzl/gjgnhz/jkzl/"; 
	$ch = curl_init(); 
	curl_setopt ($ch, CURLOPT_URL, $url); 
	curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1); 
	curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT,20); 
	$content = curl_exec($ch); 
	curl_close($ch); 
	$hasAutime=strpos($content,'美国(2014年07月21日'); 
	$hasAu=strpos($content,'美国');//防止没有下载到网页误触发,方法不可取
	
	 if (!$hasAutime && $hasAu){
	  require 'mail/mySendMail.php';
	  
	  $subject="内容有更新";
	  $body="内容有更新,请访问<a href='http://www.cnca.gov.cn/ywzl/gjgnhz/jkzl/'>http://www.cnca.gov.cn/ywzl/gjgnhz/jkzl/</a>";
	  if(mySendMail("[email protected]","name",$subject,$body)){
				 echo "有更新,已发送邮件提醒";
		 } else {
				 echo "有更新,邮件发送失败";
		 }
		 die('process abort');
	 } else {
		 echo "not modified";
	 }
	
	sleep($interval);
}while(true);
?>