#!/usr/bin/env python
#coding:utf-8
# +-------------------------------------------------------------------
# | 宝塔Linux面板
# +-------------------------------------------------------------------
# | Copyright (c) 2015-2016 宝塔软件(http://bt.cn) All rights reserved.
# +-------------------------------------------------------------------
# | Author: 黄文良 <287962566@qq.com>
# +-------------------------------------------------------------------
import sys,os,public,time,json,pwd,cgi
from BTPanel import session,request
class files:
    #检查敏感目录
    def CheckDir(self,path):
        path = path.replace('//','/');
        if path[-1:] == '/':
            path = path[:-1]
        
        nDirs = ('',
                 '/',
                '/*',
                '/www',
                '/root',
                '/boot',
                '/bin',
                '/etc',
                '/home',
                '/dev',
                '/sbin',
                '/var',
                '/usr', 
                '/tmp',
                '/sys',
                '/proc',
                '/media',
                '/mnt',
                '/opt',
                '/lib',
                '/srv', 
                '/selinux',
                '/www/server',
                '/www/server/data',
                public.GetConfigValue('logs_path'),
                public.GetConfigValue('setup_path'))

        return not path in nDirs
    
    #检测文件名
    def CheckFileName(self,filename):
        nots = ['\\','&','*','|',';','"',"'",'<','>']
        if filename.find('/') != -1: filename = filename.split('/')[-1]
        for n in nots:
            if n in filename: return False
        return True

    #名称输出过滤
    def xssencode(self,text):
        list=['<','>']
        ret=[]
        for i in text:
            if i in list:
                i=''
            ret.append(i)
        str_convert = ''.join(ret)
        text2=cgi.escape(str_convert, quote=True)
        return text2

    #上传文件
    def UploadFile(self,get):
        from werkzeug.utils import secure_filename
        from flask import request
        if sys.version_info[0] == 2: get.path = get.path.encode('utf-8');
        if not os.path.exists(get.path): os.makedirs(get.path)
        f = request.files['zunfile']
        filename = os.path.join(get.path, f.filename)
        if sys.version_info[0] == 2: filename = filename.encode('utf-8');
        s_path = get.path
        if os.path.exists(filename):s_path = filename
        p_stat = os.stat(s_path)
        f.save(filename)
        os.chown(filename,p_stat.st_uid,p_stat.st_gid)
        os.chmod(filename,p_stat.st_mode)
        public.WriteLog('TYPE_FILE','FILE_UPLOAD_SUCCESS',(filename,get['path']));
        return public.returnMsg(True,'FILE_UPLOAD_SUCCESS');



    #上传文件2
    def upload(self,args):
        if sys.version_info[0] == 2:
            args.f_name = args.f_name.encode('utf-8')
            args.f_path = args.f_path.encode('utf-8')
        if args.f_name.find('./') != -1 or args.f_path.find('./') != -1:
            return public.returnMsg(False,'错误的参数')
        if not os.path.exists(args.f_path):
            os.makedirs(args.f_path,493)
            if not 'dir_mode' in args or not 'file_mode' in args:
                self.set_mode(args.f_path)

        save_path =  os.path.join(args.f_path,args.f_name + '.' + str(int(args.f_size)) + '.upload.tmp')
        d_size = 0
        if os.path.exists(save_path): d_size = os.path.getsize(save_path)
        if d_size != int(args.f_start): return d_size
        upload_files = request.files.getlist("blob")
        f = open(save_path,'ab')
        for tmp_f in upload_files:
            f.write(tmp_f.read())
        f.close()
        f_size = os.path.getsize(save_path)
        if f_size != int(args.f_size):  return f_size
        new_name = os.path.join(args.f_path ,args.f_name)
        if os.path.exists(new_name):
            if new_name.find('.user.ini') != -1:
                public.ExecShell("chattr -i " + new_name)
            os.remove(new_name)
        os.renames(save_path, new_name)
        if 'dir_mode' in args and 'file_mode' in args:
            mode_tmp1 = args.dir_mode.split(',')
            public.set_mode(args.f_path,mode_tmp1[0])
            public.set_own(args.f_path,mode_tmp1[1])
            mode_tmp2 = args.file_mode.split(',')
            public.set_mode(new_name,mode_tmp2[0])
            public.set_own(new_name,mode_tmp2[1])

        else:
            self.set_mode(new_name)
        if new_name.find('.user.ini') != -1:
                public.ExecShell("chattr +i " + new_name)

        public.WriteLog('TYPE_FILE','FILE_UPLOAD_SUCCESS',(args.f_name,args.f_path));
        return public.returnMsg(True,'Upload Success!')

    #设置文件和目录权限
    def set_mode(self,path):
        s_path = os.path.dirname(path)
        p_stat = os.stat(s_path)
        os.chown(path,p_stat.st_uid,p_stat.st_gid)
        os.chmod(path,p_stat.st_mode)

        
    #取文件/目录列表
    def GetDir(self,get):
        if not hasattr(get,'path'):
            #return public.returnMsg(False,'错误的参数!')
            get.path = '/www/wwwroot'
        if sys.version_info[0] == 2: get.path = get.path.encode('utf-8');
        if get.path == '': get.path = '/www';
        if not os.path.exists(get.path):
            return public.ReturnMsg(False,'DIR_NOT_EXISTS')

        import pwd 
        dirnames = []
        filenames = []
        
        search = None
        if hasattr(get,'search'): search = get.search.strip().lower();
        if hasattr(get,'all'): return self.SearchFiles(get)
        
        #包含分页类
        import page
        #实例化分页类
        page = page.Page();
        info = {}
        info['count'] = self.GetFilesCount(get.path,search);
        info['row']   = 100
        info['p'] = 1
        if hasattr(get,'p'):
            try:
                info['p']     = int(get['p'])
            except:
                info['p'] = 1

        info['uri']   = {}
        info['return_js'] = ''
        if hasattr(get,'tojs'):
            info['return_js']   = get.tojs
        if hasattr(get,'showRow'):
            info['row'] = int(get.showRow);
        
        #获取分页数据
        data = {}
        data['PAGE'] = page.GetPage(info,'1,2,3,4,5,6,7,8')

        i = 0;
        n = 0;

        if not hasattr(get,'reverse'):
            for filename in os.listdir(get.path):
                filename = self.xssencode(filename)



                if search:
                    if filename.lower().find(search) == -1: continue;
                i += 1;
                if n >= page.ROW: break;
                if i < page.SHIFT: continue;
            
                try:
                    if sys.version_info[0] == 2: filename = filename.encode('utf-8');
                    filePath = get.path+'/'+filename
                    link = '';
                    if os.path.islink(filePath):
                        filePath = os.readlink(filePath);
                        link = ' -> ' + filePath;
                        if not os.path.exists(filePath): filePath = get.path + '/' + filePath;
                        if not os.path.exists(filePath): continue;
                
                    stat = os.stat(filePath)
                    accept = str(oct(stat.st_mode)[-3:]);
                    mtime = str(int(stat.st_mtime))
                    user = ''
                    try:
                        user = pwd.getpwuid(stat.st_uid).pw_name
                    except:
                        user = str(stat.st_uid)
                    size = str(stat.st_size)
                    if os.path.isdir(filePath):
                        dirnames.append(filename+';'+size+';'+mtime+';'+accept+';'+user+';'+link);
                    else:
                        filenames.append(filename+';'+size+';'+mtime+';'+accept+';'+user+';'+link);
                    n += 1;
                except:
                    continue;
        
        
            data['DIR'] = sorted(dirnames);
            data['FILES'] = sorted(filenames);
        else:
            reverse = bool(get.reverse)
            if get.reverse == 'False': reverse = False
            for file_info in  self.__list_dir(get.path,get.sort,reverse):
                filename = os.path.join(get.path,file_info['name'])
                if not os.path.exists(filename): continue
                if search:
                    if file_info['name'].lower().find(search) == -1: continue;
                i += 1;
                if n >= page.ROW: break;
                if i < page.SHIFT: continue;

                r_file = file_info['name'] + ';' + str(file_info['size']) + ';' + str(file_info['mtime']) + ';' + str(file_info['accept']) + ';' + file_info['user']+ ';' + file_info['link']
                if os.path.isdir(filename):
                    dirnames.append(r_file)
                else:
                    filenames.append(r_file)
                n += 1;

            data['DIR'] = dirnames
            data['FILES'] = filenames

        data['PATH'] = str(get.path)
        if hasattr(get,'disk'):
            import system
            data['DISK'] = system.system().GetDiskInfo();
        return data


    def __list_dir(self,path,my_sort = 'name',reverse = False):
        if not os.path.exists(path): return []
        py_v = sys.version_info[0]
        tmp_files = []
        tmp_dirs = []
        for f_name in os.listdir(path):
            if py_v == 2: f_name = f_name.encode('utf-8');
            filename = os.path.join(path,f_name)
            if not os.path.exists(filename): continue
            file_info = self.__format_stat(filename,path)
            if not file_info: continue
            if os.path.isdir(filename):
                tmp_dirs.append(file_info)
            else:
                tmp_files.append(file_info)
        tmp_dirs = sorted(tmp_dirs,key=lambda x:x[my_sort],reverse=reverse)
        tmp_files = sorted(tmp_files,key=lambda x:x[my_sort],reverse=reverse)
        
        for f in tmp_files: tmp_dirs.append(f)
        return tmp_dirs


    def __format_stat(self,filename,path):
        try:
            stat = self.__get_stat(filename,path)
            if not stat: return None
            tmp_stat = stat.split(';')
            file_info = {'name': self.xssencode(tmp_stat[0].replace('/','')),'size':int(tmp_stat[1]),'mtime':int(tmp_stat[2]),'accept':int(tmp_stat[3]),'user':tmp_stat[4],'link':tmp_stat[5]}
            return file_info
        except: return None


    def SearchFiles(self,get):
        if not hasattr(get,'path'): get.path = '/www/wwwroot'
        if sys.version_info[0] == 2: get.path = get.path.encode('utf-8');
        if not os.path.exists(get.path): get.path = '/www';
        search = get.search.strip().lower();
        my_dirs = []
        my_files = []
        count = 0
        max = 3000
        for d_list in os.walk(get.path):
            if count >= max: break;
            for d in d_list[1]:
                if count >= max: break;
                d = self.xssencode(d)
                if d.lower().find(search) != -1:
                    my_dirs.append(self.__get_stat(d_list[0] + '/' + d,get.path))
                    count += 1
                    
            for f in d_list[2]:
                if count >= max: break;
                f = self.xssencode(f)
                if f.lower().find(search) != -1:
                    my_files.append(self.__get_stat(d_list[0] + '/' + f,get.path))
                    count += 1
        data = {}
        data['DIR'] = sorted(my_dirs)
        data['FILES'] = sorted(my_files)
        data['PATH'] = str(get.path)
        data['PAGE'] = public.get_page(len(my_dirs) + len(my_files),1,max,'GetFiles')['page']
        return data


    def __get_stat(self,filename,path = None):
        stat = os.stat(filename)
        accept = str(oct(stat.st_mode)[-3:]);
        mtime = str(int(stat.st_mtime))
        user = ''
        try:
            user = pwd.getpwuid(stat.st_uid).pw_name
        except:
            user = str(stat.st_uid)
        size = str(stat.st_size)
        link = '';
        if os.path.islink(filename): link = ' -> ' + os.readlink(filename)
        tmp_path = (path + '/').replace('//','/')
        if path and tmp_path != '/':filename = filename.replace(tmp_path,'')
        return filename + ';' + size + ';' + mtime+ ';' +accept+ ';' +user+ ';' +link

    
    #计算文件数量
    def GetFilesCount(self,path,search):
        i=0;
        for name in os.listdir(path):
            if search:
                if name.lower().find(search) == -1: continue;
            i += 1;
        return i;
    
    #创建文件
    def CreateFile(self,get):
        if sys.version_info[0] == 2: get.path = get.path.encode('utf-8').strip();
        try:
            if not self.CheckFileName(get.path): return public.returnMsg(False,'FILE_NAME_SPECIAL_CHARACTRES');
            if os.path.exists(get.path):
                return public.returnMsg(False,'FILE_EXISTS')
            path = os.path.dirname(get.path)
            if not os.path.exists(path):
                os.makedirs(path)
            open(get.path,'w+').close()
            self.SetFileAccept(get.path);
            public.WriteLog('TYPE_FILE','FILE_CREATE_SUCCESS',(get.path,))
            return public.returnMsg(True,'FILE_CREATE_SUCCESS')
        except:
            return public.returnMsg(False,'FILE_CREATE_ERR')
    
    #创建目录
    def CreateDir(self,get):
        if sys.version_info[0] == 2: get.path = get.path.encode('utf-8').strip();
        try:
            if not self.CheckFileName(get.path): return public.returnMsg(False,'DIR_NAME_SPECIAL_CHARACTRES');
            if os.path.exists(get.path):
                return public.returnMsg(False,'DIR_EXISTS')
            os.makedirs(get.path)
            self.SetFileAccept(get.path);
            public.WriteLog('TYPE_FILE','DIR_CREATE_SUCCESS',(get.path,))
            return public.returnMsg(True,'DIR_CREATE_SUCCESS')
        except:
            return public.returnMsg(False,'DIR_CREATE_ERR')
    
    
    #删除目录
    def DeleteDir(self,get) :
        if sys.version_info[0] == 2: get.path = get.path.encode('utf-8');
        #if get.path.find('/www/wwwroot') == -1: return public.returnMsg(False,'此为演示服务器,禁止删除此目录!');
        if not os.path.exists(get.path):
            return public.returnMsg(False,'DIR_NOT_EXISTS')
        
        #检查是否敏感目录
        if not self.CheckDir(get.path):
            return public.returnMsg(False,'FILE_DANGER');
        
        try:
            #检查是否存在.user.ini
            #if os.path.exists(get.path+'/.user.ini'):
            #    os.system("chattr -i '"+get.path+"/.user.ini'")
            os.system("chattr -R -i " + get.path)
            if hasattr(get,'empty'):
                if not self.delete_empty(get.path): return public.returnMsg(False,'DIR_ERR_NOT_EMPTY');
            
            if os.path.exists('data/recycle_bin.pl'):
                if self.Mv_Recycle_bin(get): return public.returnMsg(True,'DIR_MOVE_RECYCLE_BIN');
            
            import shutil
            shutil.rmtree(get.path)
            public.WriteLog('TYPE_FILE','DIR_DEL_SUCCESS',(get.path,))
            return public.returnMsg(True,'DIR_DEL_SUCCESS')
        except:
            return public.returnMsg(False,'DIR_DEL_ERR')
    
    #删除 空目录 
    def delete_empty(self,path):
        if sys.version_info[0] == 2: get.path = get.path.encode('utf-8');
        for files in os.listdir(path):
            return False
        return True
    
    #删除文件
    def DeleteFile(self,get):
        if sys.version_info[0] == 2: get.path = get.path.encode('utf-8');
        if not os.path.exists(get.path):
            return public.returnMsg(False,'FILE_NOT_EXISTS')
        
        #检查是否为.user.ini
        if get.path.find('.user.ini') != -1:
            os.system("chattr -i '"+get.path+"'")
        try:
            if os.path.exists('data/recycle_bin.pl'):
                if self.Mv_Recycle_bin(get): return public.returnMsg(True,'FILE_MOVE_RECYCLE_BIN');
            os.remove(get.path)
            public.WriteLog('TYPE_FILE','FILE_DEL_SUCCESS',(get.path,))
            return public.returnMsg(True,'FILE_DEL_SUCCESS')
        except:
            return public.returnMsg(False,'FILE_DEL_ERR')
    
    #移动到回收站
    def Mv_Recycle_bin(self,get):
        rPath = '/www/Recycle_bin/'
        if not os.path.exists(rPath): os.system('mkdir -p ' + rPath);
        rFile = rPath + get.path.replace('/','_bt_') + '_t_' + str(time.time());
        try:
            import shutil
            shutil.move(get.path, rFile)
            public.WriteLog('TYPE_FILE','FILE_MOVE_RECYCLE_BIN',(get.path,))
            return True;
        except:
            public.WriteLog('TYPE_FILE','FILE_MOVE_RECYCLE_BIN_ERR',(get.path,))
            return False;
    
    #从回收站恢复
    def Re_Recycle_bin(self,get):
        rPath = '/www/Recycle_bin/'
        if sys.version_info[0] == 2: get.path = get.path.encode('utf-8');
        dFile = get.path.replace('_bt_','/').split('_t_')[0];
        get.path = rPath + get.path
        if dFile.find('BTDB_') != -1:
            import database;
            return database.database().RecycleDB(get.path);
        try:
            import shutil
            shutil.move(get.path, dFile)
            public.WriteLog('TYPE_FILE','FILE_RE_RECYCLE_BIN',(dFile,))
            return public.returnMsg(True,'FILE_RE_RECYCLE_BIN');
        except:
            public.WriteLog('TYPE_FILE','FILE_RE_RECYCLE_BIN_ERR',(dFile,))
            return public.returnMsg(False,'FILE_RE_RECYCLE_BIN_ERR');
    
    #获取回收站信息
    def Get_Recycle_bin(self,get):
        rPath = '/www/Recycle_bin/'
        if not os.path.exists(rPath): os.system('mkdir -p ' + rPath);
        data = {};
        data['dirs'] = [];
        data['files'] = [];
        data['status'] = os.path.exists('data/recycle_bin.pl');
        data['status_db'] = os.path.exists('data/recycle_bin_db.pl');
        for file in os.listdir(rPath):
            file = self.xssencode(file)
            try:
                tmp = {};
                fname = rPath + file;
                tmp1 = file.split('_bt_');
                tmp2 = tmp1[len(tmp1)-1].split('_t_');
                tmp['rname'] = file;
                tmp['dname'] = file.replace('_bt_','/').split('_t_')[0];
                tmp['name'] = tmp2[0];
                tmp['time'] = int(float(tmp2[1]));
                if os.path.islink(fname): 
                    filePath = os.readlink(fname);
                    link = ' -> ' + filePath;
                    if os.path.exists(filePath): 
                        tmp['size'] = os.path.getsize(filePath);
                    else:
                        tmp['size'] = 0;
                else:
                    tmp['size'] = os.path.getsize(fname);
                if os.path.isdir(fname):
                    data['dirs'].append(tmp);
                else:
                    data['files'].append(tmp);
            except:
                continue;
        return data;
    
    #彻底删除
    def Del_Recycle_bin(self,get):
        rPath = '/www/Recycle_bin/'
        if sys.version_info[0] == 2: get.path = get.path.encode('utf-8');
        dFile = get.path.split('_t_')[0];
        if dFile.find('BTDB_') != -1:
            import database;
            return database.database().DeleteTo(rPath+get.path);
        if not self.CheckDir(rPath + get.path):
            return public.returnMsg(False,'FILE_DANGER');
        os.system('chattr -R -i ' + rPath + get.path)
        if os.path.isdir(rPath + get.path):
            import shutil
            shutil.rmtree(rPath + get.path);
        else:
            os.remove(rPath + get.path);
        
        tfile = get.path.replace('_bt_','/').split('_t_')[0];
        public.WriteLog('TYPE_FILE','FILE_DEL_RECYCLE_BIN',(tfile,));
        return public.returnMsg(True,'FILE_DEL_RECYCLE_BIN',(tfile,));
    
    #清空回收站
    def Close_Recycle_bin(self,get):
        rPath = '/www/Recycle_bin/'
        os.system('chattr -R -i ' + rPath)
        import database,shutil;
        rlist = os.listdir(rPath)
        i = 0;
        l = len(rlist);
        for name in rlist:
            i += 1;
            path = rPath + name;
            public.writeSpeed(name,i,l);
            if name.find('BTDB_') != -1:
                database.database().DeleteTo(path);
                continue;
            if os.path.isdir(path):
                #os.system('rm -rf ' + path);
                shutil.rmtree(path);
            else:
                #os.system('rm -f ' + path);
                os.remove(path);
        public.writeSpeed(None,0,0);
        public.WriteLog('TYPE_FILE','FILE_CLOSE_RECYCLE_BIN');
        return public.returnMsg(True,'FILE_CLOSE_RECYCLE_BIN');
    
    #回收站开关
    def Recycle_bin(self,get):        
        c = 'data/recycle_bin.pl';
        if hasattr(get,'db'): c = 'data/recycle_bin_db.pl';
        if os.path.exists(c):
            os.remove(c)
            public.WriteLog('TYPE_FILE','FILE_OFF_RECYCLE_BIN');
            return public.returnMsg(True,'FILE_OFF_RECYCLE_BIN');
        else:
            public.writeFile(c,'True');
            public.WriteLog('TYPE_FILE','FILE_ON_RECYCLE_BIN');
            return public.returnMsg(True,'FILE_ON_RECYCLE_BIN');
    
    #复制文件
    def CopyFile(self,get) :
        if sys.version_info[0] == 2:
            get.sfile = get.sfile.encode('utf-8');
            get.dfile = get.dfile.encode('utf-8');
        if not os.path.exists(get.sfile):
            return public.returnMsg(False,'FILE_NOT_EXISTS')
        
        #if os.path.exists(get.dfile):
        #    return public.returnMsg(False,'FILE_EXISTS')
        
        if os.path.isdir(get.sfile):
            return self.CopyDir(get)
        
        import shutil
        try:
            shutil.copyfile(get.sfile, get.dfile)
            public.WriteLog('TYPE_FILE','FILE_COPY_SUCCESS',(get.sfile,get.dfile))
            stat = os.stat(get.sfile)
            os.chown(get.dfile,stat.st_uid,stat.st_gid)
            return public.returnMsg(True,'FILE_COPY_SUCCESS')
        except:
            return public.returnMsg(False,'FILE_COPY_ERR')
    
    #复制文件夹
    def CopyDir(self,get):
        if sys.version_info[0] == 2:
            get.sfile = get.sfile.encode('utf-8');
            get.dfile = get.dfile.encode('utf-8');
        if not os.path.exists(get.sfile):
            return public.returnMsg(False,'DIR_NOT_EXISTS');
        
        if os.path.exists(get.dfile):
            return public.returnMsg(False,'DIR_EXISTS');
        
        #if not self.CheckDir(get.dfile):
        #    return public.returnMsg(False,'FILE_DANGER');
        
        import shutil
        try:
            shutil.copytree(get.sfile, get.dfile)
            stat = os.stat(get.sfile)
            os.chown(get.dfile,stat.st_uid,stat.st_gid)
            public.WriteLog('TYPE_FILE','DIR_COPY_SUCCESS',(get.sfile,get.dfile))
            return public.returnMsg(True,'DIR_COPY_SUCCESS')
        except:
            return public.returnMsg(False,'DIR_COPY_ERR')
        
    
    
    #移动文件或目录
    def MvFile(self,get):
        if sys.version_info[0] == 2:
            get.sfile = get.sfile.encode('utf-8');
            get.dfile = get.dfile.encode('utf-8');
        if not self.CheckFileName(get.dfile): return public.returnMsg(False,'FILE_NAME_SPECIAL_CHARACTRES');
        if not os.path.exists(get.sfile):
            return public.returnMsg(False,'FILE_NOT_EXISTS')
        
        #if os.path.exists(get.dfile):
        #    return public.returnMsg(False,'FILE_EXISTS')
        
        if not self.CheckDir(get.sfile):
            return public.returnMsg(False,'FILE_DANGER');
        
        import shutil
        try:
            shutil.move(get.sfile, get.dfile)
            
            if hasattr(get,'rename'):
                public.WriteLog('TYPE_FILE','RENAME',(get.sfile,get.dfile))
                return public.returnMsg(True,'RENAME_SUCCESS')
            else:
                public.WriteLog('TYPE_FILE','MOVE_SUCCESS',(get.sfile,get.dfile))
                return public.returnMsg(True,'MOVE_SUCCESS')
        except:
            return public.returnMsg(False,'MOVE_ERR')
        
    #检查文件是否存在
    def CheckExistsFiles(self,get):
        if sys.version_info[0] == 2: get.dfile = get.dfile.encode('utf-8');
        data = [];
        filesx = [];
        if not hasattr(get,'filename'):
            filesx = json.loads(session['selected']['data']);
        else:
            filesx.append(get.filename);
        
        for fn in filesx:
            if fn == '.': continue
            filename = get.dfile + '/' + fn;
            if os.path.exists(filename):
                tmp = {}
                stat = os.stat(filename)
                tmp['filename'] = fn;
                tmp['size'] = os.path.getsize(filename);
                tmp['mtime'] = str(int(stat.st_mtime));
                data.append(tmp);
        return data;
            
    
    #获取文件内容
    def GetFileBody(self,get) :
        if sys.version_info[0] == 2: get.path = get.path.encode('utf-8');
        if not os.path.exists(get.path):
            if get.path.find('rewrite') == -1:
                return public.returnMsg(False,'FILE_NOT_EXISTS',(get.path,))
            public.writeFile(get.path,'');
        
        if os.path.getsize(get.path) > 2097152: return public.returnMsg(False,'CANT_EDIT_ONLINE_FILE');
        fp = open(get.path,'rb')
        data = {}
        data['status'] = True
        
        try:
            if fp:
                from chardet.universaldetector import UniversalDetector
                detector = UniversalDetector()
                srcBody = b""
                for line in fp.readlines():
                    detector.feed(line)
                    srcBody += line
                detector.close()
                char = detector.result
                data['encoding'] = char['encoding']
                if char['encoding'] == 'GB2312' or not char['encoding'] or char['encoding'] == 'TIS-620' or char['encoding'] == 'ISO-8859-9': data['encoding'] = 'GBK';
                if char['encoding'] == 'ascii' or char['encoding'] == 'ISO-8859-1': data['encoding'] = 'utf-8';
                if char['encoding'] == 'Big5': data['encoding'] = 'BIG5';
                if not char['encoding'] in ['GBK','utf-8','BIG5']: data['encoding'] = 'utf-8';
                try:
                    if sys.version_info[0] == 2: 
                        data['data'] = srcBody.decode(data['encoding']).encode('utf-8',errors='ignore');
                    else:
                        data['data'] = srcBody.decode(data['encoding'])
                except:
                    data['encoding'] = char['encoding'];
                    if sys.version_info[0] == 2: 
                        data['data'] = srcBody.decode(data['encoding']).encode('utf-8',errors='ignore');
                    else:
                        data['data'] = srcBody.decode(data['encoding'])
            else:
                if sys.version_info[0] == 2: 
                    data['data'] = srcBody.decode('utf-8').encode('utf-8');
                else:
                    data['data'] = srcBody.decode('utf-8')
                data['encoding'] = u'utf-8';

            return data;
        except Exception as ex:
            return public.returnMsg(False,'INCOMPATIBLE_FILECODE',(str(ex)),)
    
    
    #保存文件
    def SaveFileBody(self,get):
        if sys.version_info[0] == 2: get.path = get.path.encode('utf-8');
        if not os.path.exists(get.path):
            if get.path.find('.htaccess') == -1:
                return public.returnMsg(False,'FILE_NOT_EXISTS')
        
        try:
            isConf = -1
            if os.path.exists('/etc/init.d/nginx') or os.path.exists('/etc/init.d/httpd'):
                isConf = get.path.find('nginx');
                if isConf == -1: isConf = get.path.find('apache');
                if isConf == -1: isConf = get.path.find('rewrite');
                if isConf != -1:
                    os.system('\\cp -a '+get.path+' /tmp/backup.conf');
            
            data = get.data;
            userini = False;
            if get.path.find('.user.ini') != -1:
                userini = True;
                public.ExecShell('chattr -i ' + get.path);
                
            
            if get.path.find('/www/server/cron') != -1:
                    try:
                        import crontab
                        data = crontab.crontab().CheckScript(data);
                    except:
                        pass
            
            if get.encoding == 'ascii':get.encoding = 'utf-8';
            if sys.version_info[0] == 2:
                data = data.encode(get.encoding,errors='ignore');
                fp = open(get.path,'w+')
            else:
                data = data.encode(get.encoding,errors='ignore').decode(get.encoding);
                fp = open(get.path,'w+',encoding=get.encoding)
            fp.write(data)
            fp.close()
            
            if isConf != -1:
                isError = public.checkWebConfig();
                if isError != True:
                    os.system('\\cp -a /tmp/backup.conf '+get.path);
                    return public.returnMsg(False,'ERROR:<br><font style="color:red;">'+isError.replace("\n",'<br>')+'</font>');
                public.serviceReload();
            
            if userini: public.ExecShell('chattr +i ' + get.path);
            
            public.WriteLog('TYPE_FILE','FILE_SAVE_SUCCESS',(get.path,));
            return public.returnMsg(True,'FILE_SAVE_SUCCESS');
        except Exception as ex:
            return public.returnMsg(False,'FILE_SAVE_ERR' + str(ex));
        
    
    #文件压缩
    def Zip(self,get) :
        if not 'z_type' in get: get.z_type = 'rar'
        import panelTask
        task_obj = panelTask.bt_task()
        task_obj.create_task(public.GetMsg("COMPRESSION_FILE"),3,get.path,json.dumps({"sfile":get.sfile,"dfile":get.dfile,"z_type":get.z_type}))
        public.WriteLog("TYPE_FILE", 'ZIP_SUCCESS',(get.sfile,get.dfile));
        return public.returnMsg(True,'ADD_COMPRESSION_TO_LINEUP')
    
    
    #文件解压
    def UnZip(self,get):
        import panelTask
        if not 'password' in get:get.password = '' 
        task_obj = panelTask.bt_task()
        task_obj.create_task(public.GetMsg("DECOMPRESSION_FILE"),2,get.sfile,json.dumps({"dfile":get.dfile,"password":get.password}))
        public.WriteLog("TYPE_FILE", 'UNZIP_SUCCESS',(get.sfile,get.dfile));
        return public.returnMsg(True,'ADD_DECOMPRESSION_TO_LINEUP')
    
    
    #获取文件/目录 权限信息
    def GetFileAccess(self,get):
        if sys.version_info[0] == 2: get.filename = get.filename.encode('utf-8');
        data = {}
        try:
            import pwd
            stat = os.stat(get.filename)
            data['chmod'] = str(oct(stat.st_mode)[-3:])
            data['chown'] = pwd.getpwuid(stat.st_uid).pw_name
        except:
            data['chmod'] = 755
            data['chown'] = 'www'
        return data
    
    
    #设置文件权限和所有者
    def SetFileAccess(self,get,all = '-R'):
        if sys.version_info[0] == 2: get.filename = get.filename.encode('utf-8');
        if 'all' in get:
            if get.all == 'False': all = ''
        try:
            if not self.CheckDir(get.filename): return public.returnMsg(False,'FILE_DANGER');
            if not os.path.exists(get.filename):
                return public.returnMsg(False,'FILE_NOT_EXISTS')
            os.system('chmod '+all+' '+get.access+" '"+get.filename+"'")
            os.system('chown '+all+' '+get.user+':'+get.user+" '"+get.filename+"'")
            public.WriteLog('TYPE_FILE','FILE_ACCESS_SUCCESS',(get.filename,get.access,get.user))
            return public.returnMsg(True,'SET_SUCCESS')
        except:
            return public.returnMsg(False,'SET_ERROR')

    def SetFileAccept(self,filename):
        os.system('chown -R www:www ' + filename)
        os.system('chmod -R 755 ' + filename)
    
    
    
    #取目录大小
    def GetDirSize(self,get):
        if sys.version_info[0] == 2: get.path = get.path.encode('utf-8');
        tmp = public.ExecShell('du -sbh '+ get.path)
        return tmp[0].split()[0]

    #取目录大小2
    def get_path_size(self,get):
        if sys.version_info[0] == 2: get.path = get.path.encode('utf-8');
        data = {}
        data['path'] = get.path
        data['size'] = public.get_path_size(get.path)
        return data
    
    def CloseLogs(self,get):
        get.path = public.GetConfigValue('root_path')
        os.system('rm -f '+public.GetConfigValue('logs_path')+'/*')
        if public.get_webserver() == 'nginx':
            os.system('kill -USR1 `cat '+public.GetConfigValue('setup_path')+'/nginx/logs/nginx.pid`');
        else:
            os.system('/etc/init.d/httpd reload');
        
        public.WriteLog('TYPE_FILE','SITE_LOG_CLOSE')
        get.path = public.GetConfigValue('logs_path')
        return self.GetDirSize(get)
            
    #批量操作
    def SetBatchData(self,get):
        if sys.version_info[0] == 2: get.path = get.path.encode('utf-8');
        if get.type == '1' or get.type == '2':
            session['selected'] = get
            return public.returnMsg(True,'FILE_ALL_TIPS')
        elif get.type == '3':
            for key in json.loads(get.data):
                try:
                    if sys.version_info[0] == 2: key = key.encode('utf-8')
                    filename = get.path+'/'+key
                    if not self.CheckDir(filename): return public.returnMsg(False,'FILE_DANGER');
                    os.system('chmod -R '+get.access+" '"+filename+"'")
                    os.system('chown -R '+get.user+':'+get.user+" '"+filename+"'")
                except:
                    continue;
            public.WriteLog('TYPE_FILE','FILE_ALL_ACCESS')
            return public.returnMsg(True,'FILE_ALL_ACCESS')
        else:
            import shutil
            isRecyle = os.path.exists('data/recycle_bin.pl')
            path = get.path
            get.data = json.loads(get.data)
            l = len(get.data);
            i = 0;
            for key in get.data:
                try:
                    if sys.version_info[0] == 2: key = key.encode('utf-8')
                    filename = path + '/'+key
                    get.path = filename;
                    if not os.path.exists(filename): continue
                    i += 1;
                    public.writeSpeed(key,i,l);
                    if os.path.isdir(filename):
                        if not self.CheckDir(filename): return public.returnMsg(False,'FILE_DANGER');
                        os.system("chattr -R -i " + filename)
                        if isRecyle:
                            self.Mv_Recycle_bin(get)
                        else:
                            shutil.rmtree(filename)
                    else:
                        if key == '.user.ini':
                            if l > 1: continue
                            os.system('chattr -i ' + filename);
                        if isRecyle:
                            
                            self.Mv_Recycle_bin(get)
                        else:
                            os.remove(filename)
                except:
                    continue;
                public.writeSpeed(None,0,0);
            public.WriteLog('TYPE_FILE','FILE_ALL_DEL')
            return public.returnMsg(True,'FILE_ALL_DEL')
    
    
    #批量粘贴
    def BatchPaste(self,get):
        import shutil
        if sys.version_info[0] == 2: get.path = get.path.encode('utf-8');
        if not self.CheckDir(get.path): return public.returnMsg(False,'FILE_DANGER');
        i = 0;
        myfiles = json.loads(session['selected']['data'])
        l = len(myfiles);
        if get.type == '1':
            for key in myfiles:
                i += 1
                public.writeSpeed(key,i,l);
                try:
                    if sys.version_info[0] == 2:
                        sfile = session['selected']['path'] + '/' + key.encode('utf-8')
                        dfile = get.path + '/' + key.encode('utf-8')
                    else:
                        sfile = session['selected']['path'] + '/' + key
                        dfile = get.path + '/' + key

                    if os.path.isdir(sfile):
                        shutil.copytree(sfile,dfile)
                    else:
                        shutil.copyfile(sfile,dfile)
                    stat = os.stat(sfile)
                    os.chown(dfile,stat.st_uid,stat.st_gid)
                except:
                    continue;
            public.WriteLog('TYPE_FILE','FILE_ALL_COPY',(session['selected']['path'],get.path))
        else:
            for key in myfiles:
                try:
                    i += 1
                    public.writeSpeed(key,i,l);
                    if sys.version_info[0] == 2:
                        sfile = session['selected']['path'] + '/' + key.encode('utf-8')
                        dfile = get.path + '/' + key.encode('utf-8')
                    else:
                        sfile = session['selected']['path'] + '/' + key
                        dfile = get.path + '/' + key
                    shutil.move(sfile,dfile)
                except:
                    continue;
            public.WriteLog('TYPE_FILE','FILE_ALL_MOTE',(session['selected']['path'],get.path))
        public.writeSpeed(None,0,0);
        errorCount = len(myfiles) - i
        del(session['selected'])
        return public.returnMsg(True,'FILE_ALL',(str(i),str(errorCount)));
    
    #下载文件
    def DownloadFile(self,get):
        import panelTask
        task_obj = panelTask.bt_task()
        task_obj.create_task(public.GetMsg("DOWNLOAD_FILE"),1,get.url,get.path + '/' + get.filename)
        #if sys.version_info[0] == 2: get.path = get.path.encode('utf-8');
        #import db,time
        #isTask = '/tmp/panelTask.pl'
        #execstr = get.url +'|bt|'+get.path+'/'+get.filename
        #sql = db.Sql()
        #sql.table('tasks').add('name,type,status,addtime,execstr',('下载文件['+get.filename+']','download','0',time.strftime('%Y-%m-%d %H:%M:%S'),execstr))
        #public.writeFile(isTask,'True')
        #self.SetFileAccept(get.path+'/'+get.filename);
        public.WriteLog('TYPE_FILE','FILE_DOWNLOAD',(get.url , get.path));
        return public.returnMsg(True,'FILE_DOANLOAD')
    
    #添加安装任务
    def InstallSoft(self,get):
        import db,time
        path = public.GetConfigValue('setup_path') + '/php'
        if not os.path.exists(path): os.system("mkdir -p " + path);
        if session['server_os']['x'] != 'RHEL': get.type = '3'
        apacheVersion='false';
        if public.get_webserver() == 'apache':
            apacheVersion = public.readFile(public.GetConfigValue('setup_path')+'/apache/version.pl');
        public.writeFile('/var/bt_apacheVersion.pl',apacheVersion)
        public.writeFile('/var/bt_setupPath.conf',public.GetConfigValue('root_path'))
        isTask = '/tmp/panelTask.pl'
        execstr = "cd " + public.GetConfigValue('setup_path') + "/panel/install && /bin/bash install_soft.sh " + get.type + " install " + get.name + " "+ get.version;
        sql = db.Sql()
        if hasattr(get,'id'):
            id = get.id;
        else:
            id = None;
        sql.table('tasks').add('id,name,type,status,addtime,execstr',(None,'安装['+get.name+'-'+get.version+']','execshell','0',time.strftime('%Y-%m-%d %H:%M:%S'),execstr))
        public.writeFile(isTask,'True')
        public.WriteLog('TYPE_SETUP','PLUGIN_ADD',(get.name,get.version));
        time.sleep(0.1);
        return public.returnMsg(True,'PLUGIN_ADD');
    
    #删除任务队列
    def RemoveTask(self,get):
        try:
            name = public.M('tasks').where('id=?',(get.id,)).getField('name');
            status = public.M('tasks').where('id=?',(get.id,)).getField('status');
            public.M('tasks').delete(get.id);
            if status == '-1':
                os.system("kill `ps -ef |grep 'python panelSafe.pyc'|grep -v grep|grep -v panelExec|awk '{print $2}'`");
                os.system("kill `ps -ef |grep 'install_soft.sh'|grep -v grep|grep -v panelExec|awk '{print $2}'`");
                os.system("kill `ps aux | grep 'python task.pyc$'|awk '{print $2}'`");
                os.system('''
pids=`ps aux | grep 'sh'|grep -v grep|grep install|awk '{print $2}'`
arr=($pids)

for p in ${arr[@]}
do
    kill -9 $p
done
            ''');
            
                os.system('rm -f ' + name.replace('扫描目录[','').replace(']','') + '/scan.pl');
                isTask = '/tmp/panelTask.pl';
                public.writeFile(isTask,'True');
                os.system('/etc/init.d/bt start');
        except:
            os.system('/etc/init.d/bt start');
        return public.returnMsg(True,'PLUGIN_DEL');
    
    #重新激活任务
    def ActionTask(self,get):
        isTask = '/tmp/panelTask.pl'
        public.writeFile(isTask,'True');
        return public.returnMsg(True,'PLUGIN_ACTION');
        
    
    #卸载软件
    def UninstallSoft(self,get):
        public.writeFile('/var/bt_setupPath.conf',public.GetConfigValue('root_path'))
        get.type = '0'
        if session['server_os']['x'] != 'RHEL': get.type = '3'
        execstr = "cd " + public.GetConfigValue('setup_path') + "/panel/install && /bin/bash install_soft.sh "+get.type+" uninstall " + get.name.lower() + " "+ get.version.replace('.','');
        os.system(execstr);
        public.WriteLog('TYPE_SETUP','PLUGIN_UNINSTALL',(get.name,get.version));
        return public.returnMsg(True,"PLUGIN_UNINSTALL");
        
    
    #取任务队列进度
    def GetTaskSpeed(self,get):
        tempFile = '/tmp/panelExec.log'
        freshFile = '/tmp/panelFresh'
        import db
        find = db.Sql().table('tasks').where('status=? OR status=?',('-1','0')).field('id,type,name,execstr').find()
        if not len(find): return public.returnMsg(False,'NO_TASK_AT_LINEUP',("-2",))
        isTask = '/tmp/panelTask.pl'
        public.writeFile(isTask,'True');
        echoMsg = {}
        echoMsg['name'] = find['name']
        echoMsg['execstr'] = find['execstr']
        if find['type'] == 'download':
            try:
                tmp = public.readFile(tempFile)
                if len(tmp) < 10:
                    return public.returnMsg(False,'NO_TASK_AT_LINEUP',("-3",))
                echoMsg['msg'] = json.loads(tmp)
                echoMsg['isDownload'] = True
            except:
                db.Sql().table('tasks').where("id=?",(find['id'],)).save('status',('0',))
                return public.returnMsg(False,'NO_TASK_AT_LINEUP',("-4",))
        else:
            echoMsg['msg'] = self.GetLastLine(tempFile,20)
            echoMsg['isDownload'] = False
        
        echoMsg['task'] = public.M('tasks').where("status!=?",('1',)).field('id,status,name,type').order("id asc").select()
        return echoMsg
    
    #取执行日志
    def GetExecLog(self,get):
        return self.GetLastLine('/tmp/panelExec.log',100);
                 
    #读文件指定倒数行数
    def GetLastLine(self,inputfile,lineNum):
        result = public.GetNumLines(inputfile,lineNum)
        if len(result) < 1:
            return public.getMsg('TASK_SLEEP');
        return result
        
    
    #执行SHELL命令
    def ExecShell(self,get):
        disabled = ['vi','vim','top','passwd','su']
        get.shell = get.shell.strip()
        tmp = get.shell.split(' ');
        if tmp[0] in disabled: return public.returnMsg(False,'FILE_SHELL_ERR',(tmp[0],));
        shellStr = '''#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
cd %s
%s
''' % (get.path,get.shell)
        public.writeFile('/tmp/panelShell.sh',shellStr);
        os.system('nohup bash /tmp/panelShell.sh > /tmp/panelShell.pl 2>&1 &');
        return public.returnMsg(True,'FILE_SHELL_EXEC');
    
    #取SHELL执行结果
    def GetExecShellMsg(self,get):
        fileName = '/tmp/panelShell.pl';
        if not os.path.exists(fileName): return 'FILE_SHELL_EMPTY';
        status = not public.process_exists('bash',None,'/tmp/panelShell.sh')
        return public.returnMsg(status,public.GetNumLines(fileName,200));
    
    #文件搜索
    def GetSearch(self,get):
        if not os.path.exists(get.path): return public.returnMsg(False,'DIR_NOT_EXISTS');
        return public.ExecShell("find "+get.path+" -name '*"+get.search+"*'");

    #保存草稿
    def SaveTmpFile(self,get):
        save_path = '/www/server/panel/temp'
        if not os.path.exists(save_path): os.makedirs(save_path)
        get.path = os.path.join(save_path,public.Md5(get.path) + '.tmp')
        public.writeFile(get.path,get.body)
        return public.returnMsg(True,'HAVE_BEEN_SAVE')

    #获取草稿
    def GetTmpFile(self,get):
        self.CleanOldTmpFile()
        save_path = '/www/server/panel/temp'
        if not os.path.exists(save_path): os.makedirs(save_path)
        src_path = get.path
        get.path = os.path.join(save_path,public.Md5(get.path) + '.tmp')
        if not os.path.exists(get.path): return public.returnMsg(False,'HAVE_NOT_DRAFT')
        data = self.GetFileInfo(get.path)
        data['file'] = src_path
        if 'rebody' in get: data['body'] = public.readFile(get.path)
        return data

    #清除过期草稿
    def CleanOldTmpFile(self):
        if 'clean_tmp_file' in session: return True
        save_path = '/www/server/panel/temp'
        max_time = 86400 * 30
        now_time = time.time()
        for tmpFile in os.listdir(save_path):
            filename = os.path.join(save_path,tmpFile)
            fileInfo = self.GetFileInfo(filename)
            if now_time - fileInfo['modify_time'] > max_time: os.remove(filename)
        session['clean_tmp_file'] = True
        return True

    #取指定文件信息
    def GetFileInfo(self,path):
        if not os.path.exists(path): return False
        stat = os.stat(path)
        fileInfo = {}
        fileInfo['modify_time'] = int(stat.st_mtime)
        fileInfo['size'] = os.path.getsize(path)
        return fileInfo

    #安装rar组件
    def install_rar(self,get):
        unrar_file = '/www/server/rar/unrar'
        rar_file = '/www/server/rar/rar'
        bin_unrar = '/usr/local/bin/unrar'
        bin_rar = '/usr/local/bin/rar'
        if os.path.exists(unrar_file) and os.path.exists(bin_unrar):
            try:
                import rarfile
            except: 
                os.system("pip install rarfile")
            return True

        import platform
        os_bit = ''
        if platform.machine() == 'x86_64': os_bit = '-x64';
        download_url = public.get_url() + '/src/rarlinux'+os_bit+'-5.6.1.tar.gz';

        tmp_file = '/tmp/bt_rar.tar.gz'
        os.system('wget -O ' + tmp_file + ' ' + download_url)
        if os.path.exists(unrar_file): os.system("rm -rf /www/server/rar")
        os.system("tar xvf " + tmp_file + ' -C /www/server/')
        if os.path.exists(tmp_file): os.remove(tmp_file)
        if not os.path.exists(unrar_file): return False
                
        if os.path.exists(bin_unrar): os.remove(bin_unrar)
        if os.path.exists(bin_rar): os.remove(bin_rar)

        os.system('ln -sf ' + unrar_file + ' ' + bin_unrar)
        os.system('ln -sf ' + rar_file + ' ' + bin_rar)
        os.system("pip install rarfile")
        #public.writeFile('data/restart.pl','True')
        return True
        
       