飛信簡訊發送的PHP類(散分)
飛信發送API網上有很多,但沒有多少是我自己滿意的。很多網站提供基於Web的API調用方式向使用者提供服務,但是作為使用者我心裡還是沒底。我總是擔心自己的密碼會被某些人記錄,一直想寫一個自己用的PHP版本飛信發送程式。
因為本人沒有任何逆向基礎,同時飛信版本變化不同。從nathan在百度上發布《飛信協議分析》到現在也有3年了,且當時分析的是飛信2006版本。這中間變化太多,也使得我在寫PHP版本飛信發送程式是走了很多彎路。
我曾經拜讀過superli_198的《讓 PHP 程式利用飛信(Fetion)發免費簡訊》,但是該版本使用的通訊方式目前已經不被飛信支援,且superli_198也沒有做新的更新。我也下載過c.young[@]xicabin.com的Openfetion,但是該版本存在明顯bug,現在也不能正常使用。無奈只能硬著頭皮修改一個C# 版本的飛信發送程式。
在移植C#版本的飛信發送程式到PHP過程中,我遇到了一個關於MD5加密相關的問題,困了很多天。最後在CSDN論壇ycTIN的協助下,問題得以解決。非常感謝ycTIN。 以下是我完成的PHP版飛信簡訊發送類,截止到2010年2月17日下午4點該程式一直能正常工作。技術上沒有什麼難度,發在這裡和大家交流。
PHP code
"; /** *使用手機號碼和密碼向伺服器擷取對應的飛訊號碼資訊 **/ private $REQUEST_SSI_SIGN = "mobileno=%s&pwd=%s" ; /** *使用飛訊號碼向SIPC伺服器註冊,擷取臨時變數NONCE和SSIC的值 **/ private $REQUEST_SIPC_SIGN_NONCE = "R %s SIP-C/2.0\r\nF: %s\r\nI: 1\r\nQ: 1 R\r\nL: %d\r\n\r\n%s" ; private $REQUEST_SIPC_SIGN_NONCE_BODY = ""; /** *使用飛訊號碼和加密的密碼登入飛信SIPC伺服器 **/ private $REQUEST_SIPC_LOGIN = "R %s SIP-C/2.0\r\nF: %s\r\nI: 1\r\nQ: 2 R\r\nA: Digest response=\"%s\",cnonce=\"%s\"\r\nL: %d\r\n\r\n%s"; private $REQUEST_SIPC_LOGIN_BODY = ""; private $REQUEST_SIPC_SENDSMS = "M %s SIP-C/2.0\r\nF: %s\r\nI: 2\r\nQ: 1 M\r\nT: tel:%s\r\nN: SendSMS\r\nL: %d\r\n\r\n%s"; private $REQUEST_SIPC_LOGOUT = "R %s SIP-C/2.0\r\nF: %s\r\nI: 1 \r\nQ: 3 R\r\nX: 0\r\n\r\n"; /** [email protected] $sender 簡訊寄件者手機號 [email protected] $passwd 簡訊寄件者密碼 [email protected] $receiver 簡訊接收者手機號 [email protected] $msg 簡訊內容 **/ public function __construct($sender, $passwd, $receiver, $msg){ $this->mobile_no = $sender ; $this->fetion_pwd = $passwd; $this->SMS_RECEIVER = $receiver; $this->SMS_TEXT = $msg; $this->cookie_file = $this->mobile_no . $this->cookie_file ; file_put_contents($this->cookie_file, '') ; $this->FetionGetConfig(); // 從導航網站443連接埠擷取登入資訊 $this->FetionSocektInit(); // 初始化到SIPC的8080連接埠socket串連 $this->FetionGetSIPCNonce(); // 向伺服器註冊飛訊號,擷取關鍵變數值 if($this->FetionLogin()){ // 發送登入認證命令 $this->FetionSendSMS(); // 傳送簡訊發送命令 $this->FetionLogout(); } } /** *從導航地址擷取配置資訊 **/ private function FetionGetConfig(){ $this->REQUEST_CONFIG = sprintf($this->REQUEST_CONFIG, $this->mobile_no); $this->curl = curl_init(); curl_setopt($this->curl, CURLOPT_URL, $this->url_nav); curl_setopt($this->curl, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($this->curl, CURLOPT_COOKIEJAR, $this->cookie_file); curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($this->curl, CURLOPT_POST, 1); curl_setopt($this->curl, CURLOPT_POSTFIELDS, $this->REQUEST_CONFIG); //curl_setopt($this->curl, CURLOPT_PROXY, $this->proxy_http); // 設定Proxy 伺服器 $xml_config = curl_exec($this->curl); // 以下是從導航頁面返回的XML裡取相關資訊 file_put_contents("test3.xml", $xml_config) ; $xmlDom = new DOMDocument() ; $xmlDom->loadXML($xml_config); $fetion_server = $xmlDom->getElementsByTagName('servers'); $fetion_server->item(0)->getElementsByTagName('sipc-proxy')->item(0)->nodeValue; $this->SSI_PROXY_SIGN_IN = $fetion_server->item(0)->getElementsByTagName('ssi-app-sign-in')->item(0)->nodeValue; $this->SSI_PROXY_SIGH_OUT = $fetion_server->item(0)->getElementsByTagName('ssi-app-sign-out')->item(0)->nodeValue; $this->SSI_PROXY_SIGN_IN; // 以下擷取手機號對應的飛訊號 sprintf($this->REQUEST_SSI_SIGN, $this->mobile_no, $this->fetion_pwd) ; curl_setopt($this->curl, CURLOPT_URL, $this->SSI_PROXY_SIGN_IN); curl_setopt($this->curl, CURLOPT_POSTFIELDS, sprintf($this->REQUEST_SSI_SIGN, $this->mobile_no, $this->fetion_pwd)); $Result = curl_exec($this->curl); curl_close($this->curl); file_put_contents("test4.xml", $Result) ; $xmlDom->loadXML($Result); $uri = $xmlDom->getElementsByTagName("user")->item(0)->getAttribute("uri"); //"sip:[email protected];p=5914" if(preg_match('/^sip:(\d+)@(\S+);.*$/', $uri, $matches)){ $this->fetion_no = $matches[1] ; $this->domain_fetion = $matches[2] ; } } /** *初始化Fetion通訊Socket **/ private function FetionSocektInit(){ $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); list($ip_fetion, $port_fetion) = split(':', $this->SIPC_PROXY) ; // "221.176.31.4:8080" socket_connect($this->socket, $ip_fetion, $port_fetion) ; } /** *註冊飛訊號碼並擷取臨時變數NONCE和SSIC **/ private function FetionGetSIPCNonce(){ $REQUEST_SIPC_SIGN_NONCE = sprintf($this->REQUEST_SIPC_SIGN_NONCE, $this->domain_fetion, $this->fetion_no, strlen($this->REQUEST_SIPC_SIGN_NONCE_BODY), $this->REQUEST_SIPC_SIGN_NONCE_BODY) ; $sock_data = socket_write($this->socket, $REQUEST_SIPC_SIGN_NONCE); $buf = '' ; if (false == ($buf = socket_read($this->socket, 1000))) { echo "Line:" . __LINE__ . "socket_read() failed; reason: " . socket_strerror(socket_last_error($this->socket)) . "\n"; } $regex_ssic = '/.*nonce=\"(\\w+)\".*/s' ; if(!preg_match($regex_ssic, $buf, $matches)){ echo "Fetion Error: No nonce found in socket\n"; } $this->NONCE = strtoupper(trim($matches[1])); $regex_ssic = '/ssic\s+(.*)/s'; if (!preg_match($regex_ssic, file_get_contents($this->cookie_file), $matches)) { echo "Fetion Error: No ssic found in cookie\n"; } $this->SSIC = trim($matches[1]); }