<?php   if(!defined('DEDEINC')) exit("Request Error!");
/**
 * 織夢模塊類
 *
 * @version        $Id: dedemodule.class.php 1 10:31 2010年7月6日Z tianya $
 * @package        DedeCMS.Libraries
 * @copyright      Copyright (c) 2007 - 2010, DesDev, Inc.
 * @license        http://help.dedecms.com/usersguide/license.html
 * @link           http://www.dedecms.com
 */
require_once(DEDEINC.'/charset.func.php');
require_once(DEDEINC.'/dedeatt.class.php');

class DedeModule
{
    var $modulesPath;
	var $modulesUrl;
    var $modules;
    var $fileListNames;
    var $sysLang;
    var $moduleLang;
    function __construct($modulespath='',$modulesUrl='')
    {
        global $cfg_soft_lang;
        $this->sysLang = $this->moduleLang = $cfg_soft_lang;
        $this->fileListNames = array();
        $this->modulesPath = $modulespath;
		$this->modulesUrl = $modulesUrl;
    }
    function DedeModule($modulespath='')
    {
        $this->__construct($modulespath);
    }

    /**
     *  枚舉系統裡已經存在的模塊(緩存功能實際上只作hash與文件名的解析，在此不特別處理)
     *
     * @access    public
     * @param     string   $moduletype   模塊類型
     * @return    string
     */
    function GetModuleList($moduletype='')
    {
        if(is_array($this->modules)) return $this->modules;

        $dh = dir($this->modulesPath) or die("沒找到模塊目錄：({$this->modulesPath})！");

        $fp = @fopen($this->modulesPath.'/modulescache.php','w') or die('讀取文件權限出錯,目錄文件'.$this->modulesPath.'/modulescache.php不可寫!');

        fwrite($fp, "<"."?php\r\n");
        fwrite($fp, "global \$allmodules;\r\n");
        while($filename = $dh->read())
        {
            if(preg_match("/\.xml$/i", $filename))
            {
                $minfos = $this->GetModuleInfo(str_replace('.xml','',$filename));
                if(isset($minfos['moduletype']) && $moduletype!='' && $moduletype!=$minfos['moduletype'])
                {
                    continue;
                }
                if($minfos['hash']!='')
                {
                    $this->modules[$minfos['hash']] = $minfos;
                    fwrite($fp, '$'."GLOBALS['allmodules']['{$minfos['hash']}']='{$filename}';\r\n");
                }
            }
        }
        fwrite($fp,'?'.'>');
        fclose($fp);
        $dh->Close();
        return $this->modules;
    }

	/**
     *  從遠程獲取模塊信息
     *
     * @access    public
     * @param     string   $moduletype   模塊類型
     * @return    string
     */
    function GetModuleUrlList($moduletype='',$url='')
    {
		$dh = dir($this->modulesPath) or die("沒找到模塊目錄：({$this->modulesPath})！");
        $fp = @fopen($this->modulesPath.'/modulescache.php','w') or die('讀取文件權限出錯,目錄文件'.$this->modulesPath.'/modulescache.php不可寫!');
        $modules = unserialize(file_get_contents($url));
		if(empty($moduletype)){
			return $modules;
		}
		$return = array();
		foreach($modules as $arrow=>$data) {
			if($data['moduletype']==$moduletype)
				$return[] =  $data;
		}
		return $return;
    }
    /**
     *  轉換編碼
     *
     * @access    public
     * @param     string    $str  字符串
     * @return    string
     */
    function AppCode(&$str)
    {
        if($this->moduleLang==$this->sysLang)
        {
            return $str;
        }
        else
        {
            if($this->sysLang=='utf-8')
            {
                if($this->moduleLang=='gbk') return gb2utf8($str);
                if($this->moduleLang=='big5') return gb2utf8(big52gb($str));
            }
            else if($this->sysLang=='gbk')
            {
                if($this->moduleLang=='utf-8') return utf82gb($str);
                if($this->moduleLang=='big5') return big52gb($str);
            }
            else if($this->sysLang=='big5')
            {
                if($this->moduleLang=='utf-8') return gb2big5(utf82gb($str));
                if($this->moduleLang=='gbk') return gb2big5($str);
            }
            else
            {
                return $str;
            }
        }
    }

    /**
     *  獲得指定hash的模塊文件
     *
     * @access    public
     * @param     string  $hash  hash文件
     * @return    string
     */
    function GetHashFile($hash)
    {
        include_once($this->modulesPath.'/modulescache.php');
        if(isset($GLOBALS['allmodules'][$hash])) return $GLOBALS['allmodules'][$hash];
        else return $hash.'.xml';
    }

    /**
     *  獲得某模塊的基本信息
     *
     * @access    public
     * @param     string   $hash  hash
     * @param     string   $ftype  文件類型
     * @return    string
     */
    function GetModuleInfo($hash, $ftype='hash')
    {
        if($ftype=='file') $filename = $hash;
		else if(!empty($this->modulesUrl)) {
			$filename = $this->modulesUrl.$hash.'.xml';
		}else $filename = $this->modulesPath.'/'.$this->GetHashFile($hash);
        $start = 0;
        $minfos = array();
        $minfos['name']=$minfos['team']=$minfos['time']=$minfos['email']=$minfos['url']='';
        $minfos['hash']=$minfos['indexname']=$minfos['indexurl']='';
        $minfos['ismember']=$minfos['autosetup']=$minfos['autodel']=0;
        //$minfos['filename'] = $filename;
		if(empty($this->modulesUrl)){
			$minfos['filesize'] = filesize($filename)/1024;
			$minfos['filesize'] = number_format($minfos['filesize'],2,'.','').' Kb';
		}
        $fp = fopen($filename,'r') or die("文件 {$filename} 不存在或不可讀!");
        $n = 0;
        while(!feof($fp))
        {
            $n++;
            if($n > 30) break;
            $line = fgets($fp,256);
            if($start==0)
            {  if(preg_match("/<baseinfo/is",$line)) $start = 1; }
            else
            {
                if(preg_match("/<\/baseinfo/is",$line)) break;
                $line = trim($line);
                list($skey,$svalue) = explode('=',$line);
                $skey = trim($skey);
                $minfos[$skey] = $svalue;
            }
        }
        fclose($fp);

        if(isset($minfos['lang'])) $this->moduleLang = trim($minfos['lang']);
        else $this->moduleLang = 'gbk';

        if($this->sysLang=='gb2312') $this->sysLang = 'gbk';
        if($this->moduleLang=='gb2312') $this->moduleLang = 'gbk';

        if($this->sysLang != $this->moduleLang)
        {
            foreach($minfos as $k=>$v) $minfos[$k] = $this->AppCode($v);
        }

        return $minfos;
    }

    /**
     *  獲得某模塊的基本信息
     *
     * @access    public
     * @param     string   $hash  hash
     * @param     string   $ftype  文件類型
     * @return    string
     */
    function GetFileXml($hash, $ftype='hash')
    {
        if($ftype=='file') $filename = $hash;
        else $filename = $this->modulesPath.'/'.$this->GetHashFile($hash);
        $filexml = '';
        $fp = fopen($filename,'r') or die("文件 {$filename} 不存在或不可讀!");
        $start = 0;
        while(!feof($fp))
        {
            $line = fgets($fp,1024);
            if($start==0)
            {
                if(preg_match("/<modulefiles/is",$line))
                {
                    $filexml .= $line;
                    $start = 1;
                }
                continue;
            }
            else
            {
                $filexml .= $line;
            }
        }
        fclose($fp);
        return $filexml;
    }

    /**
     *  獲得系統文件的內容
     *  指安裝、刪除、協議文件
     *
     * @access    public
     * @param     string   $hashcode  hash碼
     * @param     string   $ntype  文件類型
     * @param     string   $enCode  是否加密
     * @return    string
     */
    function GetSystemFile($hashcode, $ntype, $enCode=TRUE)
    {
        $this->GetModuleInfo($hashcode,$ntype);
        $start = FALSE;
        $filename = $this->modulesPath.'/'.$this->GetHashFile($hashcode);
        $fp = fopen($filename,'r') or die("文件 {$filename} 不存在或不可讀!");
        $okdata = '';
        while(!feof($fp))
        {
            $line = fgets($fp,1024);
            if(!$start)
            {
                if(preg_match("#<{$ntype}#i", $line)) $start = TRUE;
            }
            else
            {
                if(preg_match("#<\/{$ntype}#i", $line)) break;
                $okdata .= $line;
                unset($line);
            }
        }
        fclose($fp);
        $okdata = trim($okdata);
        if(!empty($okdata) && $enCode) $okdata = base64_decode($okdata);
        $okdata = $this->AppCode($okdata);
        return $okdata;
    }

    /**
     *  把某系統文件轉換為文件
     *
     * @access    public
     * @param     string  $hashcode  hash碼
     * @param     string   $ntype  文件類型
     * @return    string  返回文件名
     */
    function WriteSystemFile($hashcode, $ntype)
    {
        $filename = $hashcode."-{$ntype}.php";
        $fname = $this->modulesPath.'/'.$filename;
        $filect = $this->GetSystemFile($hashcode,$ntype);
        $fp = fopen($fname,'w') or die('生成 {$ntype} 文件失敗！');
        fwrite($fp,$filect);
        fclose($fp);
        return $filename;
    }

    /**
     *  刪除系統文件
     *
     * @access    public
     * @param     string   $hashcode  hash碼
     * @param     string   $ntype  文件類型
     * @return    void
     */
    function DelSystemFile($hashcode,$ntype)
    {
        $filename = $this->modulesPath.'/'.$hashcode."-{$ntype}.php";
        unlink($filename);
    }

    /**
     *  檢查是否已經存在指定的模塊
     *
     * @access    public
     * @param     string  $hashcode  hash碼
     * @return    bool  如果存在則返回True,否則為False
     */
    function HasModule($hashcode)
    {
        $modulefile = $this->modulesPath.'/'.$this->GetHashFile($hashcode);
        if(file_exists($modulefile) && !is_dir($modulefile)) return TRUE;
        else  return FALSE;
    }

    /**
     *  讀取文件，返回編碼後的文件內容
     *
     * @access    public
     * @param     string   $filename  文件名
     * @param     string   $isremove  是否刪除
     * @return    string
     */
    function GetEncodeFile($filename,$isremove=FALSE)
    {
        $fp = fopen($filename,'r') or die("文件 {$filename} 不存在或不可讀!");
        $str = @fread($fp,filesize($filename));
        fclose($fp);
        if($isremove) @unlink($filename);
        if(!empty($str)) return base64_encode($str);
        else return '';
    }

    /**
     *  獲取模塊包裡的文件名列表
     *
     * @access    public
     * @param     string   $hashcode  hash碼
     * @return    string  返回文件列表
     */
    function GetFileLists($hashcode)
    {
        $dap = new DedeAttParse();
        $filelists = array();
        $modulefile = $this->modulesPath.'/'.$this->GetHashFile($hashcode);
        $fp = fopen($modulefile,'r') or die("文件 {$modulefile} 不存在或不可讀!");
        $i = 0;
        while(!feof($fp))
        {
            $line = fgets($fp,1024);
            if(preg_match("/^[\s]{0,}<file/i",$line))
            {
                $i++;
                $line = trim(preg_replace("/[><]/","",$line));
                $dap->SetSource($line);
                $filelists[$i]['type'] = $dap->CAtt->GetAtt('type');
                $filelists[$i]['name'] = $dap->CAtt->GetAtt('name');
            }
        }
        fclose($fp);
        return $filelists;
    }

    /**
     *  刪除已安裝模塊附帶的文件
     *
     * @access    public
     * @param     string   $hashcode   hash碼
     * @param     string   $isreplace  是否替換
     * @return    string
     */
    function DeleteFiles($hashcode,$isreplace=0)
    {
        if($isreplace==0) return TRUE;
        else
        {
            $dap = new DedeAttParse();
            $modulefile = $this->modulesPath.'/'.$this->GetHashFile($hashcode);
            $fp = fopen($modulefile,'r') or die("文件 {$modulefile} 不存在或不可讀!");
            $i = 0;
            $dirs = '';
            while(!feof($fp))
            {
                $line = fgets($fp,1024);
                if(preg_match("/^[\s]{0,}<file/i",$line))
                {
                    $i++;
                    $line = trim(preg_replace("/[><]/","",$line));
                    $dap->SetSource($line);
                    $filetype = $dap->CAtt->GetAtt('type');
                    $filename = $dap->CAtt->GetAtt('name');
                    $filename = str_replace("\\","/",$filename);
                    if($filetype=='dir'){ $dirs[] = $filename; }
                    else{ @unlink($filename); }
                }
            }
            $okdirs = array();
            if(is_array($dirs)){
                $st = count($dirs) -1;
                for($i=$st;$i>=0;$i--){  @rmdir($dirs[$i]); }
            }
            fclose($fp);
        }
        return TRUE;
    }

    /**
     *  把模塊包裡的文件寫入服務器
     *
     * @access    public
     * @param     string   $hashcode   hash碼
     * @param     string   $isreplace   是否替換
     * @return    string
     */
    function WriteFiles($hashcode, $isreplace=3)
    {
        global $AdminBaseDir;
        $dap = new DedeAttParse();
        $modulefile = $this->modulesPath.'/'.$this->GetHashFile($hashcode);
        $fp = fopen($modulefile,'r') or die("文件 {$modulefile} 不存在或不可讀!");
        $i = 0;
        while(!feof($fp))
        {
            $line = fgets($fp,1024);
            if( preg_match("/^[\s]{0,}<file/i",$line) )
            {
                $i++;
                $line = trim(preg_replace("/[><]/","",$line));
                $dap->SetSource($line);
                $filetype = $dap->CAtt->GetAtt('type');
                $filename = $dap->CAtt->GetAtt('name');
                $filename = str_replace("\\","/",$filename);
                if(!empty($AdminBaseDir)) $filename = $AdminBaseDir.$filename;
                if($filetype=='dir')
                {
                    if(!is_dir($filename))
                    {
                        @mkdir($filename,$GLOBALS['cfg_dir_purview']);
                    }
                    @chmod($filename,$GLOBALS['cfg_dir_purview']);
                }
                else
                {
                    $this->TestDir($filename);
                    if($isreplace==0) continue;
                    if($isreplace==3)
                    {
                        if(is_file($filename))
                        {
                            $copyname = @preg_replace("/([^\/]{1,}$)/","bak-$1",$filename);
                            @copy($filename,$copyname);
                        }
                    }
                    if(!empty($filename))
                    {
                        $fw = fopen($filename,'w') or die("寫入文件 {$filename} 失敗，請檢查相關目錄的權限！");
                        $ct = '';
                        while(!feof($fp))
                        {
                            $l = fgets($fp,1024);
                            if(preg_match("/^[\s]{0,}<\/file/i",trim($l))){ break; }
                            $ct .= $l;
                        }
                        $ct = base64_decode($ct);
                        if($this->sysLang!=$this->moduleLang)
                        {
                            //轉換內碼
                            if(preg_match('/\.(xml|php|inc|txt|htm|html|shtml|tpl|css)$/', $filename))
                            {
                                $ct = $this->AppCode($ct);
                            }
                            //轉換HTML編碼標識
                            if(preg_match('/\.(php|htm|html|shtml|inc|tpl)$/i', $filename))
                            {
                                if($this->sysLang=='big5') $charset = 'charset=big5';
                                else if($this->sysLang=='utf-8') $charset = 'charset=gb2312';
                                else  $charset = 'charset=gb2312';
                                $ct = preg_match("/charset=([a-z0-9-]*)/i", $charset, $ct);
                            }
                        }
                        fwrite($fw,$ct);
                        fclose($fw);
                    }
                }
            }
        }
        fclose($fp);
        return TRUE;
    }

    /**
     *  測試某文件的文件夾是否創建
     *
     * @access    public
     * @param     string   $filename  文件名稱
     * @return    string
     */
    function TestDir($filename)
    {
        $fs = explode('/',$filename);
        $fn = count($fs) - 1 ;
        $ndir = '';
        for($i=0;$i < $fn;$i++)
        {
            if($ndir!='') $ndir = $ndir.'/'.$fs[$i];
            else $ndir = $fs[$i];
            $rs = @is_dir($ndir);
            if( !$rs ) {
                @mkdir($ndir,$GLOBALS['cfg_dir_purview']);
                @chmod($ndir,$GLOBALS['cfg_dir_purview']);
            }
        }
        return TRUE;
    }

    /**
     *  獲取某個目錄或文件的打包數據
     *
     * @access    public
     * @param     string    $basedir   基本目錄
     * @param     string    $f
     * @param     string    $fp  文件指針
     * @return    bool
     */
    function MakeEncodeFile($basedir,$f,$fp)
    {
        $this->fileListNames = array();
        $this->MakeEncodeFileRun($basedir,$f,$fp);
        return TRUE;
    }

    /**
     *  測試目標文件
     *
     * @access    public
     * @param     string    $basedir   基本目錄
     * @param     string    $f
     * @return    bool
     */
    function MakeEncodeFileTest($basedir,$f)
    {
        $this->fileListNames = array();
        $this->MakeEncodeFileRunTest($basedir,$f);
        return TRUE;
    }

    /**
     *  檢測某個目錄或文件的打包數據，遞歸
     *
     * @access    public
     * @param     string    $basedir   基本目錄
     * @param     string    $f
     * @return    void
     */
    function MakeEncodeFileRunTest($basedir,$f)
    {
        $filename = $basedir.'/'.$f;
        if(isset($this->fileListNames[$f])) return;
        else if(preg_match("/Thumbs\.db/i",$f)) return;
        else $this->fileListNames[$f] = 1;
        $fileList = '';
        if(!file_exists($filename))
        {
            ShowMsg("文件或文件夾: {$filename} 不存在，無法進行編譯!","-1");
            exit();
        }
        if(is_dir($filename))
        {
            $dh = dir($filename);
            while($filename = $dh->read())
            {
                if($filename[0]=='.' || strtolower($filename)=='cvs') continue;
                $nfilename = $f.'/'.$filename;
                $this->MakeEncodeFileRunTest($basedir,$nfilename);
            }
        }
    }

    /**
     *  獲取個目錄或文件的打包數據，遞歸
     *
     * @access    public
     * @param     string    $basedir   基本目錄
     * @param     string    $f
     * @param     string    $fp  文件指針
     * @return    string
     */
    function MakeEncodeFileRun($basedir,$f,$fp)
    {
        $filename = $basedir.'/'.$f;
        if(isset($this->fileListNames[$f])) return;
        else if(preg_match("#Thumbs\.db#i", $f)) return;
        else $this->fileListNames[$f] = 1;
        $fileList = '';
        if(is_dir($filename))
        {
            $fileList .= "<file type='dir' name='$f'>\r\n";
            $fileList .= "</file>\r\n";
            fwrite($fp,$fileList);
            $dh = dir($filename);
            while($filename = $dh->read())
            {
                if($filename[0]=='.' || strtolower($filename)=='cvs') continue;
                $nfilename = $f.'/'.$filename;
                $this->MakeEncodeFileRun($basedir,$nfilename,$fp);
            }
        }
        else
        {
            $fileList .= "<file type='file' name='$f'>\r\n";
            $fileList .= $this->GetEncodeFile($filename);
            $fileList .= "\r\n</file>\r\n";
            fwrite($fp,$fileList);
        }
    }

    /**
     *  清理
     *
     * @access    public
     * @return    void
     */
    function Clear()
    {
        unset($this->modules);
        unset($this->fileList);
        unset($this->fileListNames);
    }

}//End Class