Subversion Repositories qbpwcf-lib(archive)

Rev

Rev 700 | Rev 705 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

#!/usr/bin/php
<?php

/*
        QBPWCF, Quick Build PHP website Component base on Fedora Linux.
    Copyright (C) 2015~2024 Min-Jhin,Chen

    This file is part of QBPWCF.

    QBPWCF is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    QBPWCF is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with QBPWCF.  If not, see <http://www.gnu.org/licenses/>.
    
*/

/*
封鎖來自http、https、smtp、named的惡意ip. 
*/

/*

bug log:

journalctl -a | grep ipBlockerd.php
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]: 內建白名單:169.254.1.1
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]: 附加白名單:169.254.1.1
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]: array(2) {
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:   ["status"]=>
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:   string(5) "false"
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:   ["error"]=>
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:   array(4) {
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:     ["function"]=>
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:     string(8) "fileInfo"
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:     ["argu"]=>
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:     array(3) {
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:       ["fileArgu"]=>
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:       string(47) "/usr/lib/20230429/qbpwcf/usr/bin/ipBlockerd.php"
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:       ["file"]=>
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:       string(25) "/var/log/httpd/access_log"
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:       ["web"]=>
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:       string(5) "false"
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:     }
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:     ["status"]=>
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:     string(5) "false"
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:     ["error"]=>
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:     array(7) {
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:       ["function"]=>
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:       string(19) "checkMultiFileExist"
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:       ["argu"]=>
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:       array(3) {
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:         ["fileArray"]=>
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:         array(1) {
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:           [0]=>
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:           string(25) "/var/log/httpd/access_log"
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:         }
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:         ["fileArgu"]=>
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:         string(47) "/usr/lib/20230429/qbpwcf/usr/bin/ipBlockerd.php"
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:         ["web"]=>
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:         string(5) "false"
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:       }
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:       ["varName"]=>
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:       array(1) {
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:         [0]=>
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:         string(25) "/var/log/httpd/access_log"
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:       }
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:       ["varExist"]=>
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:       array(1) {
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:         [0]=>
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:         string(5) "false"
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:       }
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:       ["varNameFullPath"]=>
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:       array(1) {
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:         [0]=>
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:         string(25) "/var/log/httpd/access_log"
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:       }
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:       ["allExist"]=>
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:       string(5) "false"
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:       ["status"]=>
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:       string(4) "true"
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:     }
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]:   }
Apr 29 20:03:38 ip-172-31-33-199.ap-northeast-1.compute.internal ipBlockerd.php[2362579]: }

*/

#指派命名空間
namespace qbpwcf;

#以該檔案的實際位置的 lib path 為 include path 首位
exec("cd ".pathinfo(__FILE__)["dirname"]."/../../;pwd;",$output,$status);
set_include_path($output[0].PATH_SEPARATOR.get_include_path());

#匯入外部套件
include("allInOne.php");

#當前路徑
$pwd=$output[0];

#unix socket 的位置
$unixSocket=$pwd."/".basename(__FILE__).".sock";

#提示unix domain socket位置
echo "unix domain socket used at ".$unixSocket;

#初始化儲存上次各log的時間
$preTimeFloat=array();

#初始化記錄 http log 最後變更的時間
$preTimeFloat["http"]=0;

#初始化記錄 https log 最後變更的時間
$preTimeFloat["https"]=0;

#初始化記錄 smtpd log 最後變更的時間
$preTimeFloat["smtp"]=0;

#初始化記錄 dovecot/imap log 最後變更的時間
$preTimeFloat["imap"]=0;

#初始化記錄 named log 最後變更的時間
$preTimeFloat["named"]=0;

#初始化不啟用debug模式
$debug=false;

#預設暫存的白名單ip有效時間為1小時
$tmp_white_ip_valid_time=1;

#說明函式
function help(){

        #印出指令說明
        echo "Usage of ".basename(__FILE__).":".PHP_EOL; 
        echo "--debug [true/false] 代表是否要啟動 debug 模式".PHP_EOL;
        echo "--exclude [white list ip] 代表白名單IP,一次只能放一個IP.".PHP_EOL;
        echo "--tmp-white-ip-valid-time [white ip value time] 指定暫時白名單的有效小時".PHP_EOL;
        echo "--client [true/false] 代表是否為 client 端,而非 server 端".PHP_EOL;
        echo "--client-add-tmp-white-ip [while ip for while] client端指令,代表要增加的暫時白名單IP".PHP_EOL;
        echo "--client-update-tmp-white-ip-valid-time [white ip value time] client端指令,代表要更新暫時白名單的有效時間,單位為小時".PHP_EOL;
        
        #結束執行
        exit;
        
        }#function help end

#函式說明:
#解析參數.
#回傳結果:
#$result["status"],執行是否正常,"true"代表正常,"false"代表不正常.
#$reuslt["error"],執行不正常結束的錯訊息陣列.
#$result["function"],當前執行的函式名稱.
#$result["content"],解析好的參數陣列.
#$result["content"][$key][$i],參數 $key 的 $i+1 個參數數值內容.
#$result["program"],字串,執行的程式名稱.
#必填參數:
#無.
#可省略參數:
#$conf["helpFunc"],如果解析的參數不成對,則要執行的函式名稱.
$conf["helpFunc"]="help";
#參考資料:
#無.
#備註:
#僅能在命令列底下執行.
#以後可將參數 --a--b 的名稱與後面的數值 $value 存成 $result["a"]["b"][$i]=$value .
$parseArgu=cmd::parseArgu($conf);
unset($conf);

#如果執行失敗
if($parseArgu["status"]==="false"){

        #設置執行失敗
        $result["status"]="false";

        #設置錯誤訊息
        $result["error"]=$parseArgu;

        #印出結果
        var_dump($result);
        
        #結束執行
        exit(1);

        }#if end

#檢查參數
#函式說明:
#檢查必填與可省略的參數,可省略參數可指定預設要給與什麼數值內容.
#回傳結果:
#$result["status"],執行是否正常,"true"代表正常,"false"代表不正常.
#$result["error"],執行不正常結束的錯訊息陣列.
#$result["simpleError"],簡單表示的錯誤訊息.
#$result["function"],當前執行的函式名稱.
#$result["argu"],設置給予的參數.
#$result["passed"],識別要檢查的全體變數是否存在以及型態是否正確的變數,"true"代表檢查全部通過;"false"代表檢查不通過
#$result[$shouldBeCheckedVarName]["varExist"],所檢查的變數是否存在,"false"代表不存在;"true"代表存在
#$result[$shouldBeCheckedVarName]["varType"],所檢查的變數型態是否正確,"false"代表錯誤;"true"代表正確
#$result[$shouldBeCheckedVarName]["error"],每個參數設定的錯誤訊息
#$result["shouldNotBeEmpty"],不應該為空字串或控陣列的變數.
#$result["argu"],字串陣列,目前輸入的參數名稱陣列.
#$result["legalVarName"],字串陣列,合法可用的參數名稱陣列.
#$result["notNeedVar"],字串陣列,多餘的參數名稱.
#必填參數:
#$conf["varInput"],陣列變數,要檢查的陣列變數,請在要檢查的參數前面加上&,這樣變動的結果才能被套用。
$conf["varInput"]=&$parseArgu["content"];
#$conf["referenceVarKey"],字串,$conf參數後面的key值,用於移除不要的參考陣列.
$conf["referenceVarKey"]="variableCheck::checkArguments";
#可省略參數:
#$conf["mustBeFilledVariableName"],爲必填參數的變數名稱陣列,形態爲陣列變數,元素數量需要跟"mustBeFilledVariableType"參數的元素數量一致,例如: $conf["mustBeFilledVariableName"] = array("id","account","password");
#$conf["mustBeFilledVariableName"]=array();
#$conf["mustBeFilledVariableType"],爲必填參數的變數陣列應該爲何種變數形態,形態爲陣列,元素數量需要跟"mustBeFilledVariableName"參數的元素數量一致,例如: $conf["mustBeFilledVariableType"] = array("string",integer,"double","resource","object"); , null代表不指定變數形態.
#$conf["mustBeFilledVariableType"]=array();
#$conf["canBeEmptyString"],字串,必填變數內容如果是空字串就不能算是有設置的話,請設為"false",預設爲"true",可以為空字串.
#$conf["canBeEmptyString"]="false";
#$conf["canNotBeEmpty"],字串陣列,哪些必填參數的內容不得為空字串或空陣列,僅當$conf["canBeEmptyString"]為"true"時會生效.
#$conf["canNotBeEmpty"]=array();
#$conf["canBeEmpty"],字串陣列,哪些必填參數的內容可為空字串或空陣列,僅當$conf["canBeEmptyString"]為"false"時會生效.
#$conf["canBeEmpty"]=array();
#$conf["skipableVariableCanNotBeEmpty"],字串陣列,哪些可省略參數不可以為空字串或空陣列.
$conf["skipableVariableCanNotBeEmpty"]=array("debug","exclude","tmp-white-ip-valid-time","client","tmp-white-ip-valid-time","client-add-tmp-white-ip","client-update-tmp-white-ip-valid-time");
#$conf["skipableVariableName"],陣列字串,爲可省略參數的變數名稱陣列,形態爲陣列變數,例如: $conf["skipableVariableName"] = array("id","account","password");
$conf["skipableVariableName"]=array("debug","exclude","tmp-white-ip-valid-time","client","tmp-white-ip-valid-time","client-add-tmp-white-ip","client-update-tmp-white-ip-valid-time");
#$conf["skipableVariableType"],陣列字串,爲可省略參數的變數名稱陣列,形態爲陣列變數,例如: $conf["skipableVariableType"] = array("string",integer,"double");
$conf["skipableVariableType"]=array("array","array","array","array","array","array","array");
#$conf["skipableVarDefaultValue"],字串陣列,每個不存在的可省略變數要初始化為什麼,null與代表不指定,若預設值是參數之一,請將$conf["mustBeFilledVar"]改成"\$conf["\mustBeFilledVar\"]".
$conf["skipableVarDefaultValue"]=array(array("false"),null,array("1"),null,null,null,null);
#$conf["disallowAllSkipableVarIsEmpty"],字串,是否允許每個可省略參數都為空字串,預設為"true"允許,反之為"false".
$conf["disallowAllSkipableVarIsEmpty"]="false";
#$conf["disallowAllSkipableVarIsEmptyArray"],字串,是否允許每個可省略參數都為空陣列,預設為"true"允許,反之為"false".
$conf["disallowAllSkipableVarIsEmptyArray"]="false";
#$conf["arrayCountEqualCheck"],字串陣列,為檢查哪些陣列參數的元素數量要一樣,$conf["arrayCountEqualCheck"][$i]=array()為第$i組key為哪些的變數其元素數量要相等.
#$conf["arrayCountEqualCheck"][]=array();
#參考資料:
#array_keys=>http://php.net/manual/en/function.array-keys.php
#備註:
#無.
$checkArguments=variableCheck::checkArguments($conf);
unset($conf);

#如果檢查參數失敗
if($checkArguments["status"]==="false"){

        #設置執行失敗
        $result["status"]="false";

        #設置錯誤訊息
        $result["error"]=$checkArguments;

        #印出結果
        var_dump($result);
        
        #結束執行,跟回傳錯誤代碼.
        exit(1);

        }#if end
        
#如果檢查參數不通過
if($checkArguments["passed"]==="false"){

        #設置執行失敗
        $result["status"]="false";

        #設置錯誤訊息
        $result["error"]=$checkArguments;

        #印出結果
        var_dump($result);
        
        #結束執行,跟回傳錯誤代碼.
        exit(1);

        }#if end

#如果有debug參數
if(isset($parseArgu["content"]["debug"])){

        #如果第一個參數為 "--debug true"
        if($parseArgu["content"]["debug"][0]==="true"){

                #設置debug模式
                $debug=true;

                #comment
                echo "debug mode".PHP_EOL;

                }#if end        

        }#if end

#如果有client參數
if(isset($parseArgu["content"]["client"])){

        #預設沒有id
        $clientId="";

        #如果第一個參數為 "--client true"
        if($parseArgu["content"]["client"][0]==="true"){

                #函式說明:
                #連線到 unixDomainSockServer 提供的 unix domain socket.
                #回傳結果:
                #$result["status"],"true"代表執行正常;"false"代表執行不正常.
                #$result["error"],錯誤訊息陣列.
                #$result["function"],當前執行的函式名稱.
                #$result["content"],取得的回應.
                #必填參數:
                #$conf["sock"],字串,要連線的unix domain socket.
                $conf["sock"]=$unixSocket;
                #可省略參數:
                #$conf["id"],字串,取得的id,若無此值,則會得到新的數值.
                #$conf["id"]="";
                #$conf["cmd"],字串,要執行的指令,當$conf["id"]參數合法時,才會執行.
                #$conf["cmd"]="";
                #$conf["param"],參數陣列.
                #$conf["param"]=array();
                #$conf["escaped"],字串,param參數是否已經escaped了,預設為"false",反之為"true".
                #$conf["escaped"]="true";
                #$conf["custom"],陣列,要客制化傳輸的內容,會覆蓋以上除了$conf["id"]以外的可省略參數.
                #$conf["custom"]=array();
                #參考資料:
                #http://php.net/manual/en/function.stream-socket-client.php
                #http://php.net/manual/en/function.stream-get-contents.php
                #備註:
                #無.
                $unixDomainSockClient=sock::unixDomainSockClient($conf);
                unset($conf);
                
                #如果執行失敗
                if($unixDomainSockClient["status"]==="false"){
                
                        #設置執行失敗
                        $result["status"]="false";

                        #設置錯誤訊息
                        $result["error"]=$unixDomainSockClient;

                        #印出結果
                        var_dump($result);
                        
                        #結束執行,跟回傳錯誤代碼.
                        exit(1);
                
                        }#if end

                #取得會應
                $res=$unixDomainSockClient["content"];

                #解析回應
                $res=(array)json_decode(trim($res));
                
                #debug
                #var_dump($res);
                
                #如果沒有給予 id
                if(!isset($res["id"])){
                
                        #設置執行失敗
                        $result["status"]="false";

                        #設置錯誤訊息
                        $result["error"][]="id not found";

                        #設置錯誤訊息
                        $result["error"][]=$res;

                        #印出結果
                        var_dump($result);
                        
                        #結束執行,跟回傳錯誤代碼.
                        exit(1);
                
                        }#if end
                        
                #取得用戶端的id   
                $clientId=$res["id"];   
                
                }#if end
                
        #如果有要增加暫時的白名單
        if(isset($parseArgu["content"]["client-add-tmp-white-ip"])){
        
                #針對每個要增加暫時的白名單
                foreach($parseArgu["content"]["client-add-tmp-white-ip"] as $tmpWhilteIp){
                        
                        #要求 server 端增加暫時白名單 - start
                        
                        #產生要傳遞的陣列
                        $request=array();
                        
                        #設置要新增暫時白名單的ip
                        $request["add-tmp-white-ip"]=$tmpWhilteIp;
                        
                        #函式說明:
                        #連線到 unixDomainSockServer 提供的 unix domain socket.
                        #回傳結果:
                        #$result["status"],"true"代表執行正常;"false"代表執行不正常.
                        #$result["error"],錯誤訊息陣列.
                        #$result["function"],當前執行的函式名稱.
                        #$result["content"],取得的回應.
                        #必填參數:
                        #$conf["sock"],字串,要連線的unix domain socket.
                        $conf["sock"]=$unixSocket;
                        #可省略參數:
                        #$conf["id"],字串,取得的id,若無此值,則會得到新的數值.
                        $conf["id"]=$clientId;
                        #$conf["cmd"],字串,要執行的指令,當$conf["id"]參數合法時,才會執行.
                        #$conf["cmd"]="";
                        #$conf["param"],參數陣列.
                        #$conf["param"]=array();
                        #$conf["escaped"],字串,param參數是否已經escaped了,預設為"false",反之為"true".
                        #$conf["escaped"]="true";
                        #$conf["custom"],陣列,要客制化傳輸的內容,會覆蓋以上除了$conf["id"]以外的可省略參數.
                        $conf["custom"]=$request;
                        #參考資料:
                        #http://php.net/manual/en/function.stream-socket-client.php
                        #http://php.net/manual/en/function.stream-get-contents.php
                        #備註:
                        #無.
                        $unixDomainSockClient=sock::unixDomainSockClient($conf);
                        unset($conf);
                        
                        #debug
                        #var_dump($unixDomainSockClient);
                        
                        #如果執行失敗
                        if($unixDomainSockClient["status"]==="false"){
                        
                                #設置執行失敗
                                $result["status"]="false";

                                #設置錯誤訊息
                                $result["error"]=$unixDomainSockClient;

                                #印出結果
                                var_dump($result);
                                
                                #結束執行,跟回傳錯誤代碼.
                                exit(1);
                        
                                }#if end

                        #取得回應
                        $res=$unixDomainSockClient["content"];

                        #解析回應
                        $res=(array)json_decode(trim($res));
                                
                        #要求 server 端增加暫時白名單 - end   
                                
                        }#foreach end
        
                }#if end
                
        #如果有要更新暫存白名單的有效時間
        if(isset($parseArgu["content"]["client-update-tmp-white-ip-valid-time"])){
        
                #要求 server 端更新暫時白名單的期限 - start
        
                #產生要傳遞的陣列
                $request=array();
                
                #設置要更新暫時白名單ip的有效時間
                $request["update-tmp-white-ip-valid-time"]=$parseArgu["content"]["client-update-tmp-white-ip-valid-time"][0];
                
                #函式說明:
                #連線到 unixDomainSockServer 提供的 unix domain socket.
                #回傳結果:
                #$result["status"],"true"代表執行正常;"false"代表執行不正常.
                #$result["error"],錯誤訊息陣列.
                #$result["function"],當前執行的函式名稱.
                #$result["content"],取得的回應.
                #必填參數:
                #$conf["sock"],字串,要連線的unix domain socket.
                $conf["sock"]=$unixSocket;
                #可省略參數:
                #$conf["id"],字串,取得的id,若無此值,則會得到新的數值.
                $conf["id"]=$clientId;
                #$conf["cmd"],字串,要執行的指令,當$conf["id"]參數合法時,才會執行.
                #$conf["cmd"]="";
                #$conf["param"],參數陣列.
                #$conf["param"]=array();
                #$conf["escaped"],字串,param參數是否已經escaped了,預設為"false",反之為"true".
                #$conf["escaped"]="true";
                #$conf["custom"],陣列,要客制化傳輸的內容,會覆蓋以上除了$conf["id"]以外的可省略參數.
                $conf["custom"]=$request;
                #參考資料:
                #http://php.net/manual/en/function.stream-socket-client.php
                #http://php.net/manual/en/function.stream-get-contents.php
                #備註:
                #無.
                $unixDomainSockClient=sock::unixDomainSockClient($conf);
                unset($conf);
                
                #如果執行失敗
                if($unixDomainSockClient["status"]==="false"){
                
                        #設置執行失敗
                        $result["status"]="false";

                        #設置錯誤訊息
                        $result["error"]=$unixDomainSockClient;

                        #印出結果
                        var_dump($result);
                        
                        #結束執行,跟回傳錯誤代碼.
                        exit(1);
                
                        }#if end

                #取得回應
                $res=$unixDomainSockClient["content"];

                #解析回應
                $res=(array)json_decode(trim($res));
        
                #要求 server 端更新暫時白名單的期限 - end
        
                }#if end

        #client端程式結束,結束執行
        exit;
        
        }#if end

#取得自己對外的ip
#函式說明:
#依據提供查詢IP服務的網站取得伺服器對外的IP.
#回傳結果:
#$result["status"],執行是否正常,"true"代表正常,"false"代表不正常.
#$result["function"],當前執行的函數名稱.
#$result["error"],錯誤訊息陣列.
#$result["content"],伺服端對外的IP.
#必填參數:
#$conf["fileArgu"],字串,php變數__FILE__的內容,亦即該檔案在檔案系統的絕對路徑
$conf["fileArgu"]=__FILE__;
#可省略參數:
#無.
#備註:
#需要有網站支援此服務.
$getServerRealIP=csInformation::getServerRealIP($conf);
unset($conf);   

#如果執行失敗
if($getServerRealIP["status"]==="false"){

        #設置執行失敗
        $result["status"]="false";

        #設置錯誤訊息
        $result["error"]=$getServerRealIP;

        #印出結果
        var_dump($result);
        
        #結束執行,跟回傳錯誤代碼.
        exit(1);

        }#if end

#預設的白名單為自己對外的IP
$excludeIp=array($getServerRealIP["content"][0]);       

#提示內建白名單
echo "內建白名單:".$excludeIp[0].PHP_EOL;
        
#如果有 exclude 參數
if(isset($parseArgu["content"]["exclude"])){

        #針對每個白名單 IP
        foreach($parseArgu["content"]["exclude"] as $whiteIp){
                
                #提示內建白名單
                echo "附加白名單:".$whiteIp.PHP_EOL;
                
                #取得每個指定要排除的ip
                $excludeIp[]=$whiteIp;
        
                }#foreach end
        
        }#if end

#暫時的白名單
$excludeIpsTmp=array();

#函式說明:
#建立 unix domain socket server, 僅提供具備檔案存取權限的用戶使用,預設提供可以下達任何指令的功能.
#回傳結果:
#$result["status"],"true"代表執行正常;"false"代表執行不正常.
#$result["error"],錯誤訊息陣列.
#$result["function"],當前執行的函式名稱.
#必填參數:
#$conf["sock"],字串,socket檔案要放在哪邊,名稱為何.
$conf["sock"]=$unixSocket;
#$conf["fileArgu"],字串,變數__FILE__的內容.
$conf["fileArgu"]=__FILE__;
#可省略參數:
#$conf["changeOwner"],字串,要將socket檔案的擁有着權限進行修改."user.group"代表擁有者帳號為user,群組為group.
#$conf["changeOwner"]="";
#$conf["changePermission"],字串,要將socket檔案的權限設為多少.ex: 0666(所有帳戶都有存取的權限) 或 0660(僅有擁有者與群組帳戶有存取的權限) 或 0600(只有擁有者有權限執行).
$conf["changePermission"]="0666";
#$conf["sessionTimeout"],字串,當連線結束後於下一次連線間隔多久就算session timeout,server端會將記錄移除,client端需要重新拿取id,預設為300秒.
#$conf["sessionTimeout"]="300";
#$conf["addOnProcessFunc"],字串陣列,增加用於處理 json request 的函式名稱,給予的參數為array("request"=>收到的json訊息,"sock"=>用戶的socket).
$conf["addOnProcessFunc"]=array("\qbpwcf\addOnProcessFunc");
#$conf["funcToRunWhenIdle"],字串陣列,當沒有事件產生時,要執行的函式名稱,給予參數為array("client"=>所有用戶).
$conf["funcToRunWhenIdle"]=array("\qbpwcf\\funcToRunWhenIdle");
#$conf["infoToFunction"],陣列,需要增加給addOnProcessFunc跟funcToRunWhenIdle函式的資訊,在函式中其參數的info鍵值.
$conf["infoToFunction"]=array("debug"=>&$debug,"preTimeFloat"=>&$preTimeFloat,"excludeIpsTmp"=>&$excludeIpsTmp,"excludeIp"=>&$excludeIp,"tmp-white-ip-valid-time"=>&$tmp_white_ip_valid_time);
#參考資料:
#http://php.net/manual/en/function.stream-socket-server.php
#備註:
#無.
$unixDomainSockServer=sock::unixDomainSockServer($conf);
unset($conf);

#當執行失敗
if($unixDomainSockServer["status"]==="false"){

        #設置執行失敗
        $result["status"]="false";

        #設置錯誤訊息
        $result["error"]=$unixDomainSockServer;

        #印出結果
        var_dump($result);
        
        #結束執行,跟回傳錯誤代碼.
        exit(1);

        }#if end

#收到訊息要怎麼處理的函式
function addOnProcessFunc($params){

        #取得收到的json訊息
        $receivedJson=json_decode($params["request"]);

        #取得用戶的 socket
        $clientSock=$params["socket"];
        
        #預設不 debug
        $debug=false;

        #如果有debug鍵數值.
        if(isset($params["info"]["debug"])){
        
                #設置之
                $debug=$params["info"]["debug"];
        
                }#if end
        
        #debug mode
        if($debug){
        
                #debug
                var_dump(__LINE__,$params);

                }#if end
        
        #如果解析 json 失敗
        if(json_last_error()!==JSON_ERROR_NONE){
        
                #初始化結果
                $result=array();
        
                #設置執行失敗
                $result["status"]="false";
                
                #記錄錯誤訊息
                $result["error"]=json_last_error_msg();
                
                #回覆錯誤訊息
                fwrite($clientSock,json_encode($result).PHP_EOL);
        
                #初始化要回傳的訊息
                $result=array();
                
                #設置不繼續執行
                $result["continue"]="false";
        
                #回傳結果
                return $result;
        
                }#if end
                
        #轉存成array
        $receivedArray=(array)$receivedJson;

        #debug mode
        if($debug){
        
                #debug
                var_dump(__LINE__,$receivedArray);

                }#if end

        #如果要新增暫時的ip白名單
        if(isset($receivedArray["add-tmp-white-ip"])){
        
                #函式說明:
                #將多個多維陣列串聯起來,key從0開始排序.
                #回傳的結果:
                #$result["status"],"true"表執行正常,"false"代表執行不正常.
                #$result["error"],錯誤訊息陣列.
                #$result["function"],當前執行的函數.
                #$result["content"],合併好的一維陣列.
                #必填參數
                #$conf["inputArray"],陣列,要合併的一維陣列變數,例如:=array($array1,$array2);
                $conf["inputArray"]=array($params["info"]["excludeIpsTmp"],array(array("ip"=>$receivedArray["add-tmp-white-ip"],"unixtime"=>time())));
                #可省略參數:
                #$conf["allowRepeat"],字串,預設為"false",不允許重複的結果;反之為"true".
                #$conf["allowRepeat"]="true";
                #$conf["equalKeyStruc"],字串陣列,若 allowRepeat 參數為 "false", 這該參數生效.該參數為用來判斷每個陣列的哪個鍵值路徑底下的數值相同時要進行取代,後者會取代前者.
                $conf["equalKeyStruc"]=array("ip");
                #參考資料:
                #無.
                #備註:
                #無.
                $mergeMultiDimensionArray=arrays::mergeMultiDimensionArray($conf);
                unset($conf);
                
                #debug mode
                if($debug){
                
                        #debug
                        var_dump(__LINE__,$mergeMultiDimensionArray);
                
                        }#if end
                
                #如果運行失敗
                if($mergeMultiDimensionArray["status"]==="false"){
                
                        #設置執行失敗
                        $result["status"]="false";

                        #設置錯誤訊息
                        $result["error"]=$mergeMultiDimensionArray;

                        #印出結果
                        var_dump($result);
                        
                        #結束執行
                        exit(1);
                
                        }#if end
                
                #更新白名單
                $params["info"]["excludeIpsTmp"]=$mergeMultiDimensionArray["content"];
        
                #debug mode
                if($debug){
                        
                        #debug
                        var_dump(__LINE__,$params["info"]["excludeIpsTmp"]);
        
                        }#if end
        
                }#if end
                
        #反之收到 update-tmp-white-ip-valid-time
        if(isset($receivedArray["update-tmp-white-ip-valid-time"])){
        
                #更新白名單有效時間
                $params["info"]["tmp-white-ip-valid-time"]=$receivedArray["update-tmp-white-ip-valid-time"];
                        
                #debug mode
                if($debug){
                        
                        #debug
                        var_dump(__LINE__,$params["info"]["tmp-white-ip-valid-time"]);
        
                        }#if end
                
                }#if end
                        
        #初始化要回傳的結果
        $result=array();        
                
        #設置執行正常
        $result["status"]="true";       
                
        #設置可以繼續執行後面的函式
        $result["continue"]="true";
        
        #回傳結果
        return $result;

        }#function addOnProcessFunc end

#沒事時要重複執行的函式
function funcToRunWhenIdle($params){

        #預設不 debug
        $debug=false;

        #如果有debug鍵數值.
        if(isset($params["info"]["debug"])){
        
                #設置之
                $debug=$params["info"]["debug"];
        
                }#if end

        #debug mode
        if($debug){

                #comment
                echo "new round".PHP_EOL;
                
                }#if end

        #debug mode
        if($debug){

                #comment
                echo "\$preHttpTimeFloat:".$params["info"]["preTimeFloat"]["http"].PHP_EOL;
                echo "\$preHttpsTimeFloat:".$params["info"]["preTimeFloat"]["https"].PHP_EOL;
                echo "\$preSmtpTimeFloat:".$params["info"]["preTimeFloat"]["smtp"].PHP_EOL;
                echo "\$preImapTimeFloat:".$params["info"]["preTimeFloat"]["imap"].PHP_EOL;
                echo "\$preNamedTimeFloat:".$params["info"]["preTimeFloat"]["named"].PHP_EOL;

                }#if end

        #函數說明:
        #依據取得檔案的擁有着資訊
        #回傳結果:
        #$result["status"],"true"爲建立成功,"false"爲建立失敗.
        #$result["error"],錯誤訊息陣列.
        #$result["function"],函數名稱. 
        #$result["content"],檔案資訊陣列.
        #$result["content"]["is_folder"],是否為目錄,"true"代表是,"false"代表不是.   
        #$result["content"]["ownerPerm"],檔案擁有者權限資訊.
        #$result["content"]["groupPerm"],檔案歸屬群組權限資訊.
        #$result["content"]["otherPerm"],檔案對於其他身份使用者的權限資訊.
        #$result["content"]["subElementCount"],目錄底下的檔案目錄數量.
        #$result["content"]["ownerName"],檔案擁有着資訊.
        #$result["content"]["groupName"],檔案所屬擁有着資訊.
        #$result["content"]["size"],檔案大小.
        #$result["content"]["modifyDate"],檔案變更年月日.
        #$result["content"]["modifyTime"],檔案變更時分秒.
        #$result["content"]["modifyTimeFloat"],檔案變更時間秒的float數值.
        #$result["content"]["timezone"],檔案變更時間的時區與UTC的差距.
        #必填參數:
        #$conf["fileArgu"],字串,當前檔案的位置亦即__FILE__的內容.
        $conf["fileArgu"]=__FILE__;
        #$conf["file"],字串,要查看擁有者資訊的檔案.
        $conf["file"]="/var/log/httpd/access_log";
        $conf["web"]="false";
        #參考資料:
        #fileowner=>http://php.net/manual/en/function.fileowner.php
        #posix_getpwuid=>http://php.net/manual/en/function.posix-getpwuid.php
        $httpFileInfo=fileAccess::fileInfo($conf);
        unset($conf);
        
        #如果運行失敗
        if($httpFileInfo["status"]==="false"){
        
                #設置執行失敗
                $result["status"]="false";

                #設置錯誤訊息
                $result["error"]=$httpFileInfo;

                #印出結果
                var_dump($result);
                
                #結束執行
                exit(1);
        
                }#if end
                
        #debug
        if($debug){
                
                #comment
                echo "accessed file /var/log/httpd/access_log".PHP_EOL;
                
                }#if end
        
        #記錄 http log 的最後變更時間
        $httpTimeFloat=$httpFileInfo["content"]["modifyTimeFloat"];
        
        #函數說明:
        #依據取得檔案的擁有着資訊
        #回傳結果:
        #$result["status"],"true"爲建立成功,"false"爲建立失敗.
        #$result["error"],錯誤訊息陣列.
        #$result["function"],函數名稱. 
        #$result["content"],檔案資訊陣列.
        #$result["content"]["is_folder"],是否為目錄,"true"代表是,"false"代表不是.   
        #$result["content"]["ownerPerm"],檔案擁有者權限資訊.
        #$result["content"]["groupPerm"],檔案歸屬群組權限資訊.
        #$result["content"]["otherPerm"],檔案對於其他身份使用者的權限資訊.
        #$result["content"]["subElementCount"],目錄底下的檔案目錄數量.
        #$result["content"]["ownerName"],檔案擁有着資訊.
        #$result["content"]["groupName"],檔案所屬擁有着資訊.
        #$result["content"]["size"],檔案大小.
        #$result["content"]["modifyDate"],檔案變更年月日.
        #$result["content"]["modifyTime"],檔案變更時分秒.
        #$result["content"]["modifyTimeFloat"],檔案變更時間秒的float數值.
        #$result["content"]["timezone"],檔案變更時間的時區與UTC的差距.
        #必填參數:
        #$conf["fileArgu"],字串,當前檔案的位置亦即__FILE__的內容.
        $conf["fileArgu"]=__FILE__;
        #$conf["file"],字串,要查看擁有者資訊的檔案.
        $conf["file"]="/var/log/httpd/ssl_access_log";
        $conf["web"]="false";
        #參考資料:
        #fileowner=>http://php.net/manual/en/function.fileowner.php
        #posix_getpwuid=>http://php.net/manual/en/function.posix-getpwuid.php
        $httpsFileInfo=fileAccess::fileInfo($conf);
        unset($conf);

        #如果運行失敗
        if($httpsFileInfo["status"]==="false"){
        
                #設置執行失敗
                $result["status"]="false";

                #設置錯誤訊息
                $result["error"]=$httpsFileInfo;

                #印出結果
                var_dump($result);
                
                #結束執行
                exit(1);
        
                }#if end

        #debug
        if($debug){
                
                #comment
                echo "accessed file /var/log/httpd/ssl_access_log".PHP_EOL;
                
                }#if end
                
        #記錄 https log 的最後變更時間
        $httpsTimeFloat=$httpsFileInfo["content"]["modifyTimeFloat"];
        
        #透過運行以下指令取得最後變更時間
        #journalctl -a -e | grep postfix/smtpd | grep " connect from unknown" | tail -n 1
        #涵式說明:
        #呼叫shell執行系統命令,並取得回傳的內容.
        #回傳的結果:
        #$result["status"],執行是否正常,"true"代表正常,"false"代表不正常.
        #$result["error"],錯誤訊息陣列.
        #$result["function"],當前執行的函數名稱.
        #$result["argu"],使用的參數.
        #$result["cmd"],執行的指令內容.
        #$result["fullCmd"],如果參數 $conf["inBackGround"] 為 "true" 則會回傳該值.
        #$result["output"],爲執行完二元碼後的輸出陣列,若 $conf["inBackGround"] 為 "true",則為當下的輸出.
        #$result["tmpFileOutput"],儲存輸出的暫存檔案名稱,若 $conf["inBackGround"] 為 "true" 則會回傳該值.
        #$result["running"],是否還在執行.
        #$result["pid"],pid.
        #$result["statusCode"],執行結束後的代碼.
        #必填的參數
        #$conf["command"],字串,要執行的指令與.
        $conf["external::callShell"]["command"]="journalctl";
        #$conf["fileArgu"],字串,變數__FILE__的內容.
        $conf["external::callShell"]["fileArgu"]=__FILE__;              
        #可省略參數:
        #$conf["argu"],陣列字串,指令搭配的參數,預設為空陣列.
        $conf["external::callShell"]["argu"]=array("-a","-e","|","grep","postfix/smtps/smtpd","|","grep"," connect from unknown","|","tail","-n","1");
        #$conf["arguIsAddr"],陣列字串,指令搭配的哪些參數為路徑,為路徑的參數會進行轉換以便符合呼叫當前函數的位置,預設不指定,若有3個參數,其中第3個參數為路徑,則表示為array("false","false","true").
        #$conf["arguIsAddr"]=array();   
        #$conf["pre"],陣列,要在本指令前執行的每個指令與參數.
        #$conf["pre"][$i]["cmd"],字串,要在本指令前執行的第$i+1個指令.
        #$conf["pre"][$i]["param"],陣列字串,要在本指令前執行的第$i+1個指令的參數.
        #$conf["enablePrintDescription"],字串,是否要印出$conf["printDescription"]的內容,"true"代表要,"false"代表不要,預設為"false".
        #$conf["enablePrintDescription"]="true";
        #$conf["printDescription"],字串,執行該外部程式前要印出來的的文字,預設為$conf["command"]的內容加上使用的$conf["argu"]參數.
        #$conf["printDescription"]="";
        #$conf["escapeshellarg"],字串,是否要啟用過濾參數,用了比較安全,但可能會出錯,"true"為啟用,"false"為不啟用,預設為"false".
        $conf["external::callShell"]["escapeshellarg"]="true";
        #$conf["username"],字串,要用什麼使用者來執行,預設為執行php的使用者,該參數不適用於apache環境.
        #$conf["username"]="";
        #$conf["password"],字串,root的使用者密碼,預設不使用密碼,該參數不適用於apache環境.
        #$conf["password"]="";
        #$conf["useScript"],字串,是否要啟用Linux的script指令來記錄輸出,"true"代表要,Fedora的selinux會擋住該操作;"false"代表不要,預設為"false".
        #$conf["useScript"]="";
        #$conf["logFilePath"],字串,當 $conf["useScript"] 為 "true" 時,輸出的內容要暫存到哪裡,預設為 "/tmp/.qbpwcf_tmp/external/callShell/".
        #$conf["logFilePath"]=".qbpwcf_tmp/external/callShell/";
        #$conf["inBackGround"],字串,是否要在背景執行,且不會等待程式執行結束再執行下一個指令,"true"代表是,"false"代表不要,預設為"false",如果$conf["command"]有用「;」區隔的多個指令將會出錯.
        #$conf["inBackGround"]="";
        #$conf["getErr"],字串,"true"代表將錯誤輸出變成標準輸出,反之"false"為不變動.
        #$conf["getErr"]="false";
        #備註:
        #不是所有指令都能用apache的身份執行,目前已知java,javac指令無法執行,使用root身份可能會被selinux阻擋.
        #參考資料:
        #exec=>http://php.net/manual/en/function.exec.php
        #escapeshellcmd=>http://php.net/manual/en/function.escapeshellcmd.php
        #escapeshellarg=>http://php.net/manual/en/function.escapeshellarg.php
        $callShell=external::callShell($conf["external::callShell"]);
        unset($conf["external::callShell"]);
        
        #如果執行失敗
        if($callShell["status"]==="false"){
        
                #設置執行失敗
                $result["status"]="false";

                #設置錯誤訊息
                $result["error"]=$callShell;

                #回傳結果
                var_dump($result);
                
                #結束執行
                exit(1);
                
                }#if end
        
        #debug
        if($debug){
                
                #comment
                echo "accessed file postfix/smtp log".PHP_EOL;
                
                }#if end
        
        #取得行數
        $lineCount=count($callShell["output"]);
        
        #如果行數等於1
        if($lineCount===1){
        
                #debug
                if($debug){
                        
                        #最新一筆惡意IP
                        #echo "最新一筆惡意IP:".PHP_EOL;
                        #var_dump($callShell["output"]);
                        
                        }#if end
                
                #Aug 09 07:12:09 www.qbpwcf.org postfix/smtpd[20829]: connect from unknown[185.234.219.62]
                #Apr 04 15:11:16 mail.qbpwcf.org postfix/smtps/smtpd[10145]: connect from unknown[212.70.149.72]
                $unProcesedStr=$callShell["output"];
                
                #分割字串
                #涵式說明:
                #將多個固定格式的字串分開,並回傳分開的結果
                #回傳的參數:
                #$result["status"],執行成功與否,若爲"true",代表執行成功,若爲"false"代表執失敗。
                #$result["error"],錯誤訊息陣列.
                #$result["function"],當前執行的函數名稱.
                #$result["spiltString"][$i]["oriStr"],爲第i個字串的原始內容
                #$result["spiltString"][$i]["dataArray"],爲第($i+1)個字串分割後的字串陣列
                #$result["spiltString"][$i]["dataArray"][$j],爲第($i+1)的分割好的字串的第($j+1)段內容
                #$result["spiltString"][$i]["dataCounts"],爲第($i+1)個字串分割後總共分成幾段
                #必填的參數:
                #$conf["stringIn"],字串陣列,要處理的字串陣列.
                $conf["stringProcess::spiltMutiString"]["stringIn"]=$unProcesedStr;
                #$conf["spiltSymbol"],字串,爲要以哪個符號作爲分割.
                $conf["stringProcess::spiltMutiString"]["spiltSymbol"]=" ";
                $spiltMutiString=stringProcess::spiltMutiString($conf["stringProcess::spiltMutiString"]);
                unset($conf["stringProcess::spiltMutiString"]);
                
                #如果分割失敗
                if($spiltMutiString["status"]==="false"){
                
                        #設置執行失敗
                        $result["status"]="false";

                        #設置錯誤訊息
                        $result["error"]=$spiltMutiString;

                        #印出結果
                        var_dump($result);
                        
                        #停止執行
                        exit(1);
                        
                        }#if end
                
                #記錄 smtpd log 最後變更的時間
                $smtpTimeFloat=$spiltMutiString["spiltString"][0]["dataArray"][2];
                
                }#if end        
        
        #反之沒變動
        else{
        
                #沿用上次時間
                $smtpTimeFloat=$params["info"]["preTimeFloat"]["smtp"];
                
                }#else ebd
                
        #透過運行以下指令取得最後變更時間
        #journalctl -a -e | grep dovecot | grep " failed" | tail -n 1
        #涵式說明:
        #呼叫shell執行系統命令,並取得回傳的內容.
        #回傳的結果:
        #$result["status"],執行是否正常,"true"代表正常,"false"代表不正常.
        #$result["error"],錯誤訊息陣列.
        #$result["function"],當前執行的函數名稱.
        #$result["argu"],使用的參數.
        #$result["cmd"],執行的指令內容.
        #$result["fullCmd"],如果參數 $conf["inBackGround"] 為 "true" 則會回傳該值.
        #$result["output"],爲執行完二元碼後的輸出陣列,若 $conf["inBackGround"] 為 "true",則為當下的輸出.
        #$result["tmpFileOutput"],儲存輸出的暫存檔案名稱,若 $conf["inBackGround"] 為 "true" 則會回傳該值.
        #$result["running"],是否還在執行.
        #$result["pid"],pid.
        #$result["statusCode"],執行結束後的代碼.
        #必填的參數
        #$conf["command"],字串,要執行的指令與.
        $conf["external::callShell"]["command"]="journalctl";
        #$conf["fileArgu"],字串,變數__FILE__的內容.
        $conf["external::callShell"]["fileArgu"]=__FILE__;              
        #可省略參數:
        #$conf["argu"],陣列字串,指令搭配的參數,預設為空陣列.
        $conf["external::callShell"]["argu"]=array("-a","-e","|","grep","dovecot","|","grep"," failed","|","tail","-n","1");
        #$conf["arguIsAddr"],陣列字串,指令搭配的哪些參數為路徑,為路徑的參數會進行轉換以便符合呼叫當前函數的位置,預設不指定,若有3個參數,其中第3個參數為路徑,則表示為array("false","false","true").
        #$conf["arguIsAddr"]=array();   
        #$conf["pre"],陣列,要在本指令前執行的每個指令與參數.
        #$conf["pre"][$i]["cmd"],字串,要在本指令前執行的第$i+1個指令.
        #$conf["pre"][$i]["param"],陣列字串,要在本指令前執行的第$i+1個指令的參數.
        #$conf["enablePrintDescription"],字串,是否要印出$conf["printDescription"]的內容,"true"代表要,"false"代表不要,預設為"false".
        #$conf["enablePrintDescription"]="true";
        #$conf["printDescription"],字串,執行該外部程式前要印出來的的文字,預設為$conf["command"]的內容加上使用的$conf["argu"]參數.
        #$conf["printDescription"]="";
        #$conf["escapeshellarg"],字串,是否要啟用過濾參數,用了比較安全,但可能會出錯,"true"為啟用,"false"為不啟用,預設為"false".
        $conf["external::callShell"]["escapeshellarg"]="true";
        #$conf["username"],字串,要用什麼使用者來執行,預設為執行php的使用者,該參數不適用於apache環境.
        #$conf["username"]="";
        #$conf["password"],字串,root的使用者密碼,預設不使用密碼,該參數不適用於apache環境.
        #$conf["password"]="";
        #$conf["useScript"],字串,是否要啟用Linux的script指令來記錄輸出,"true"代表要,Fedora的selinux會擋住該操作;"false"代表不要,預設為"false".
        #$conf["useScript"]="";
        #$conf["logFilePath"],字串,當 $conf["useScript"] 為 "true" 時,輸出的內容要暫存到哪裡,預設為 "/tmp/.qbpwcf_tmp/external/callShell/".
        #$conf["logFilePath"]=".qbpwcf_tmp/external/callShell/";
        #$conf["inBackGround"],字串,是否要在背景執行,且不會等待程式執行結束再執行下一個指令,"true"代表是,"false"代表不要,預設為"false",如果$conf["command"]有用「;」區隔的多個指令將會出錯.
        #$conf["inBackGround"]="";
        #$conf["getErr"],字串,"true"代表將錯誤輸出變成標準輸出,反之"false"為不變動.
        #$conf["getErr"]="false";
        #備註:
        #不是所有指令都能用apache的身份執行,目前已知java,javac指令無法執行,使用root身份可能會被selinux阻擋.
        #參考資料:
        #exec=>http://php.net/manual/en/function.exec.php
        #escapeshellcmd=>http://php.net/manual/en/function.escapeshellcmd.php
        #escapeshellarg=>http://php.net/manual/en/function.escapeshellarg.php
        $callShell=external::callShell($conf["external::callShell"]);
        unset($conf["external::callShell"]);
        
        #如果執行失敗
        if($callShell["status"]==="false"){
        
                #設置執行失敗
                $result["status"]="false";

                #設置錯誤訊息
                $result["error"]=$callShell;

                #回傳結果
                var_dump($result);
                
                #結束執行
                exit(1);
                
                }#if end
        
        #debug
        if($debug){
                
                #comment
                echo "accessed file dovecot/imap log".PHP_EOL;
                
                }#if end
        
        #取得行數
        $lineCount=count($callShell["output"]);
        
        #如果行數等於1
        if($lineCount===1){
        
                #debug
                if($debug){
                        
                        #最新一筆惡意IP
                        #echo "最新一筆惡意IP:".PHP_EOL;
                        #var_dump($callShell["output"]);
                        
                        }#if end
                
                #Feb 23 07:44:29 localhost.localdomain dovecot[1427]: imap-login: Disconnected (no auth attempts in 0 secs): user=<>, rip=101.136.215.61, lip=169.254.1.1, TLS handshaking: SSL_accept() failed: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown: SSL alert number 46, session=<9oMMYvW7ihhliNc9>
                $unProcesedStr=$callShell["output"];
                
                #分割字串
                #涵式說明:
                #將多個固定格式的字串分開,並回傳分開的結果
                #回傳的參數:
                #$result["status"],執行成功與否,若爲"true",代表執行成功,若爲"false"代表執失敗。
                #$result["error"],錯誤訊息陣列.
                #$result["function"],當前執行的函數名稱.
                #$result["spiltString"][$i]["oriStr"],爲第i個字串的原始內容
                #$result["spiltString"][$i]["dataArray"],爲第($i+1)個字串分割後的字串陣列
                #$result["spiltString"][$i]["dataArray"][$j],爲第($i+1)的分割好的字串的第($j+1)段內容
                #$result["spiltString"][$i]["dataCounts"],爲第($i+1)個字串分割後總共分成幾段
                #必填的參數:
                #$conf["stringIn"],字串陣列,要處理的字串陣列.
                $conf["stringProcess::spiltMutiString"]["stringIn"]=$unProcesedStr;
                #$conf["spiltSymbol"],字串,爲要以哪個符號作爲分割.
                $conf["stringProcess::spiltMutiString"]["spiltSymbol"]=" ";
                $spiltMutiString=stringProcess::spiltMutiString($conf["stringProcess::spiltMutiString"]);
                unset($conf["stringProcess::spiltMutiString"]);
                
                #如果分割失敗
                if($spiltMutiString["status"]==="false"){
                
                        #設置執行失敗
                        $result["status"]="false";

                        #設置錯誤訊息
                        $result["error"]=$spiltMutiString;

                        #印出結果
                        var_dump($result);
                        
                        #停止執行
                        exit(1);
                        
                        }#if end
                        
                #記錄 smtpd log 最後變更的時間
                $imapTimeFloat=$spiltMutiString["spiltString"][0]["dataArray"][2];
                
                }#if end
                
        #反之沒變動
        else{
        
                #沿用上次時間
                $imapTimeFloat=$params["info"]["preTimeFloat"]["imap"];
                
                }#else ebd
        
        #透過 journalctl -a -e | grep named | tail -n 1 來取得最後一筆 dns 查詢記錄        
        #涵式說明:
        #呼叫shell執行系統命令,並取得回傳的內容.
        #回傳的結果:
        #$result["status"],執行是否正常,"true"代表正常,"false"代表不正常.
        #$result["error"],錯誤訊息陣列.
        #$result["function"],當前執行的函數名稱.
        #$result["argu"],使用的參數.
        #$result["cmd"],執行的指令內容.
        #$result["fullCmd"],如果參數 $conf["inBackGround"] 為 "true" 則會回傳該值.
        #$result["output"],爲執行完二元碼後的輸出陣列,若 $conf["inBackGround"] 為 "true",則為當下的輸出.
        #$result["tmpFileOutput"],儲存輸出的暫存檔案名稱,若 $conf["inBackGround"] 為 "true" 則會回傳該值.
        #$result["running"],是否還在執行.
        #$result["pid"],pid.
        #$result["statusCode"],執行結束後的代碼.
        #必填的參數
        #$conf["command"],字串,要執行的指令與.
        $conf["external::callShell"]["command"]="journalctl";
        #$conf["fileArgu"],字串,變數__FILE__的內容.
        $conf["external::callShell"]["fileArgu"]=__FILE__;              
        #可省略參數:
        #$conf["argu"],陣列字串,指令搭配的參數,預設為空陣列.
        $conf["external::callShell"]["argu"]=array("-a","-e","|","grep"," named","|","tail","-n","1");
        #$conf["arguIsAddr"],陣列字串,指令搭配的哪些參數為路徑,為路徑的參數會進行轉換以便符合呼叫當前函數的位置,預設不指定,若有3個參數,其中第3個參數為路徑,則表示為array("false","false","true").
        #$conf["arguIsAddr"]=array();   
        #$conf["pre"],陣列,要在本指令前執行的每個指令與參數.
        #$conf["pre"][$i]["cmd"],字串,要在本指令前執行的第$i+1個指令.
        #$conf["pre"][$i]["param"],陣列字串,要在本指令前執行的第$i+1個指令的參數.
        #$conf["enablePrintDescription"],字串,是否要印出$conf["printDescription"]的內容,"true"代表要,"false"代表不要,預設為"false".
        #$conf["enablePrintDescription"]="true";
        #$conf["printDescription"],字串,執行該外部程式前要印出來的的文字,預設為$conf["command"]的內容加上使用的$conf["argu"]參數.
        #$conf["printDescription"]="";
        #$conf["escapeshellarg"],字串,是否要啟用過濾參數,用了比較安全,但可能會出錯,"true"為啟用,"false"為不啟用,預設為"false".
        $conf["external::callShell"]["escapeshellarg"]="true";
        #$conf["username"],字串,要用什麼使用者來執行,預設為執行php的使用者,該參數不適用於apache環境.
        #$conf["username"]="";
        #$conf["password"],字串,root的使用者密碼,預設不使用密碼,該參數不適用於apache環境.
        #$conf["password"]="";
        #$conf["useScript"],字串,是否要啟用Linux的script指令來記錄輸出,"true"代表要,Fedora的selinux會擋住該操作;"false"代表不要,預設為"false".
        #$conf["useScript"]="";
        #$conf["logFilePath"],字串,當 $conf["useScript"] 為 "true" 時,輸出的內容要暫存到哪裡,預設為 "/tmp/.qbpwcf_tmp/external/callShell/".
        #$conf["logFilePath"]=".qbpwcf_tmp/external/callShell/";
        #$conf["inBackGround"],字串,是否要在背景執行,且不會等待程式執行結束再執行下一個指令,"true"代表是,"false"代表不要,預設為"false",如果$conf["command"]有用「;」區隔的多個指令將會出錯.
        #$conf["inBackGround"]="";
        #$conf["getErr"],字串,"true"代表將錯誤輸出變成標準輸出,反之"false"為不變動.
        #$conf["getErr"]="false";
        #備註:
        #不是所有指令都能用apache的身份執行,目前已知java,javac指令無法執行,使用root身份可能會被selinux阻擋.
        #參考資料:
        #exec=>http://php.net/manual/en/function.exec.php
        #escapeshellcmd=>http://php.net/manual/en/function.escapeshellcmd.php
        #escapeshellarg=>http://php.net/manual/en/function.escapeshellarg.php
        $callShell=external::callShell($conf["external::callShell"]);
        unset($conf["external::callShell"]);
        
        #如果執行失敗
        if($callShell["status"]==="false"){
        
                #設置執行失敗
                $result["status"]="false";

                #設置錯誤訊息
                $result["error"]=$callShell;

                #回傳結果
                var_dump($result);
                
                #結束執行
                exit(1);
                
                }#if end
        
        #debug
        if($debug){
                
                #comment
                echo "accessed file named log".PHP_EOL;
                
                }#if end
        
        #取得行數
        $lineCount=count($callShell["output"]);
        
        #如果行數等於1
        if($lineCount===1){
        
                #debug
                if($debug){
                        
                        #最新一筆惡意IP
                        #echo "最新一筆惡意IP:".PHP_EOL;
                        #var_dump($callShell["output"]);
                        
                        }#if end
                
                #Aug 09 07:12:09 www.qbpwcf.org postfix/smtpd[20829]: connect from unknown[185.234.219.62]
                $unProcesedStr=$callShell["output"];
                
                #分割字串
                #涵式說明:
                #將多個固定格式的字串分開,並回傳分開的結果
                #回傳的參數:
                #$result["status"],執行成功與否,若爲"true",代表執行成功,若爲"false"代表執失敗。
                #$result["error"],錯誤訊息陣列.
                #$result["function"],當前執行的函數名稱.
                #$result["spiltString"][$i]["oriStr"],爲第i個字串的原始內容
                #$result["spiltString"][$i]["dataArray"],爲第($i+1)個字串分割後的字串陣列
                #$result["spiltString"][$i]["dataArray"][$j],爲第($i+1)的分割好的字串的第($j+1)段內容
                #$result["spiltString"][$i]["dataCounts"],爲第($i+1)個字串分割後總共分成幾段
                #必填的參數:
                #$conf["stringIn"],字串陣列,要處理的字串陣列.
                $conf["stringProcess::spiltMutiString"]["stringIn"]=$unProcesedStr;
                #$conf["spiltSymbol"],字串,爲要以哪個符號作爲分割.
                $conf["stringProcess::spiltMutiString"]["spiltSymbol"]=" ";
                $spiltMutiString=stringProcess::spiltMutiString($conf["stringProcess::spiltMutiString"]);
                unset($conf["stringProcess::spiltMutiString"]);
                
                #如果分割失敗
                if($spiltMutiString["status"]==="false"){
                
                        #設置執行失敗
                        $result["status"]="false";

                        #設置錯誤訊息
                        $result["error"]=$spiltMutiString;

                        #印出結果
                        var_dump($result);
                        
                        #停止執行
                        exit(1);
                        
                        }#if end
                        
                #記錄 named log 最後變更的時間
                $namedTimeFloat=$spiltMutiString["spiltString"][0]["dataArray"][2];     
                
                }#if end
                
        #反之無變動
        else{

                #沿用上次的時間
                $namedTimeFloat=$params["info"]["preTimeFloat"]["named"];
        
                }#else end
                
        #debug mode
        if($debug){

                #comment
                echo "\$httpTimeFloat:".$httpTimeFloat.PHP_EOL;
                echo "\$httpsTimeFloat:".$httpsTimeFloat.PHP_EOL;
                echo "\$smtpTimeFloat:".$smtpTimeFloat.PHP_EOL;
                echo "\$imapTimeFloat:".$imapTimeFloat.PHP_EOL;
                echo "\$namedTimeFloat:".$namedTimeFloat.PHP_EOL;

                }#if end

        #第一次運行,不做事.
        if($params["info"]["preTimeFloat"]["http"]===0 && $params["info"]["preTimeFloat"]["https"]===0 && $params["info"]["preTimeFloat"]["smtp"]===0 && $params["info"]["preTimeFloat"]["imap"]===0 && $params["info"]["preTimeFloat"]["named"]===0){

                #將本次http記錄覆寫到上次的記錄
                $params["info"]["preTimeFloat"]["http"]=$httpTimeFloat;
                
                #將本次https記錄覆寫到上次的記錄
                $params["info"]["preTimeFloat"]["https"]=$httpsTimeFloat;
                
                #將本次smtps記錄覆寫到上次的記錄
                $params["info"]["preTimeFloat"]["smtp"]=$smtpTimeFloat;
                
                #將本次imaps記錄覆寫到上次的記錄
                $params["info"]["preTimeFloat"]["imap"]=$imapTimeFloat;
                
                #將本次nmaed記錄覆寫到上次的記錄
                $params["info"]["preTimeFloat"]["named"]=$namedTimeFloat;
                
                #睡一秒
                sleep(1);

                #debug mode
                if($debug){

                        #comment
                        echo "第一次運行,不做事".PHP_EOL;

                        }#if end

                #結束執行
                return;
        
                }#if end
                
        #非第一次運行,且 http 與 https log 都沒變動過.
        else if($params["info"]["preTimeFloat"]["http"]===$httpTimeFloat && $params["info"]["preTimeFloat"]["https"]===$httpsTimeFloat && $params["info"]["preTimeFloat"]["smtp"]===$smtpTimeFloat && $params["info"]["preTimeFloat"]["imap"]===$imapTimeFloat && $params["info"]["preTimeFloat"]["named"]===$namedTimeFloat ){
        
                #睡一秒
                sleep(1);
                
                #debug mode
                if($debug){

                        #comment
                        echo "Log檔無變動,不做事".PHP_EOL;

                        }#if end
                
                #結束執行
                return;
        
                }#else end

        #debug mode
        if($debug){

                #comment
                echo "Log檔有變動,要做事".PHP_EOL;
                echo "\$preHttpTimeFloat:".$params["info"]["preTimeFloat"]["http"].PHP_EOL;
                echo "\$preHttpsTimeFloat:".$params["info"]["preTimeFloat"]["https"].PHP_EOL;
                echo "\$preSmtpTimeFloat:".$params["info"]["preTimeFloat"]["smtp"].PHP_EOL;
                echo "\$preImapTimeFloat:".$params["info"]["preTimeFloat"]["imap"].PHP_EOL;
                echo "\$preNamedTimeFloat:".$params["info"]["preTimeFloat"]["named"].PHP_EOL;
                echo "\$httpTimeFloat:".$httpTimeFloat.PHP_EOL;
                echo "\$httpsTimeFloat:".$httpsTimeFloat.PHP_EOL;
                echo "\$smtpTimeFloat:".$smtpTimeFloat.PHP_EOL;
                echo "\$imapTimeFloat:".$imapTimeFloat.PHP_EOL;
                echo "\$namedTimeFloat:".$namedTimeFloat.PHP_EOL;

                }#if end

        #將本次http記錄覆寫到上次的記錄
        $params["info"]["preTimeFloat"]["http"]=$httpTimeFloat;
        
        #將本次https記錄覆寫到上次的記錄
        $params["info"]["preTimeFloat"]["https"]=$httpsTimeFloat;
        
        #初始化儲存暫時ip白名單的陣列
        $excludeIpsTmp=array();
        
        #針對每個暫時白名單ip
        foreach($params["info"]["excludeIpsTmp"] as $index=>$tmpIpInfo){
        
                #若暫存白名單已經過期
                if($tmpIpInfo["unixtime"]<time()-$params["info"]["tmp-white-ip-valid-time"]*3600){
                
                        #卸除之
                        unset($params["info"]["excludeIpsTmp"][$index]);
                
                        }#if end
                        
                #反之
                else{
                
                        #變成 $excludeIpsTmp 的內容        
                        $excludeIpsTmp[]=$params["info"]["excludeIpsTmp"][$index]["ip"];
                
                        }#else end
                
                }#foreach end
                
        #函式說明:
        #將多個多維陣列串聯起來,key從0開始排序.
        #回傳的結果:
        #$result["status"],"true"表執行正常,"false"代表執行不正常.
        #$result["error"],錯誤訊息陣列.
        #$result["function"],當前執行的函數.
        #$result["content"],合併好的一維陣列.
        #必填參數
        #$conf["inputArray"],陣列,要合併的一維陣列變數,例如:=array($array1,$array2);
        $conf["inputArray"]=array($params["info"]["excludeIp"],$excludeIpsTmp);
        #可省略參數:
        #$conf["allowRepeat"],字串,預設為"false",不允許重複的結果;反之為"true".
        #$conf["allowRepeat"]="true";
        #$conf["equalKeyStruc"],字串陣列,若 allowRepeat 參數為 "false", 這該參數生效.該參數為用來判斷每個陣列的哪個鍵值路徑底下的數值相同時要進行取代,後者會取代前者.
        #$conf["equalKeyStruc"]=array();
        #參考資料:
        #無.
        #備註:
        #無.
        $mergeMultiDimensionArray=arrays::mergeMultiDimensionArray($conf);
        unset($conf);
        
        #如果執行失敗
        if($mergeMultiDimensionArray["status"]==="false"){
        
                #設置執行失敗
                $result["status"]="false";

                #設置錯誤訊息
                $result["error"]=$mergeMultiDimensionArray;

                #回傳結果
                var_dump($result);
                
                #結束執行
                exit(1);
                
                }#if end
                
        #debug
        #var_dump(__LINE__,$mergeMultiDimensionArray);
                
        #涵式說明:
        #檢查 httpd 與 postfix/smtpd 的 log 與 named 的log 把惡意連線的 IP 用防火牆阻阻擋
        #回傳結果:
        #$result["status"],執行是否正常,"true"代表正常,"false"代表不正常.
        #$result["error"],錯誤訊息.
        #$result["function"],當前執行的函數名稱.
        #$result["argu"],所使用的參數.
        #$result["found"],是否有找到符合的檔案,"true"代表有;"false"代表沒有.
        #$result["content"],找到的檔案陣列.
        #必填參數:
        #$conf["fileArgu"],字串,php變數__FILE__的內容,亦即該檔案在檔案系統的絕對路徑
        $conf["fileArgu"]=__FILE__;
        #可省略參數:
        #$conf["excludeIp"],字串陣列,白名單ip陣列.
        $conf["excludeIp"]=$mergeMultiDimensionArray["content"];
        #$conf["logPath"],字串,httpd的log位置,預設為 "/var/log/httpd"
        #$conf["logPath"]="";
        #$conf["username"],字串,要用什麼使用者來執行,預設為root使用者
        #$conf["username"]="";
        #$conf["password"],字串,root使用者的密碼,
        #$conf["password"]="";
        #$conf["getIplistOnly"],字串,是否不阻擋IP只取得有問題的IP,預設為"false",要阻擋IP;"true"代表只取得有問題的IP.
        #$conf["getIplistOnly"]="true";
        $blockAcctackIp=cmd::blockAcctackIp($conf);
        unset($conf);

        #如果執行失敗
        if($blockAcctackIp["status"]==="false"){

                #設置執行失敗
                $result["status"]="false";

                #設置錯誤訊息
                $result["error"]=$blockAcctackIp;

                #印出結果
                var_dump($result);
                
                #結束執行
                exit(1);

                }#if end
                
        #如果有執行封鎖IP的情形
        if(isset($blockAcctackIp["content"])){

                #comment
                echo "本次封鎖IP的結果如下:".PHP_EOL;
                var_dump($blockAcctackIp["content"]);
                var_dump($blockAcctackIp["reason"]);

                }#if end
                
        #睡一秒
        sleep(1);
                
        #結束執行
        return;
        
        }//function funcToRunWhenIdle end

?>