def __init__(self, root='/tmp', outer_show=None): # local relative if not os.path.isdir(root): raise InternalError('input not a path') self.root_directory = root self.console_log(0, 'local root set to: ' + root) # connect relative self.status = self.STATUS_OFFLINE self.trans_mode = self.MODE_PASV self.control_sock = None self.data_sock = None if outer_show is not None and not callable(outer_show): raise InternalError('function given to Session not callable') self.outer_show = outer_show # file relative self.TRANSMITTING = False self.trans_size = 0 self.trans_status = self.TRANS_NO_TASK self.current_upload = {} self.current_download = {} # server relative self.server_ip = '' self.server_port = 0 self.server_root = '' self.server_current_dir = ''
def list_server_file(self): if self.trans_mode == self.MODE_PORT: self.data_port() else: self.data_pasv() self.send_msg('LIST') code = self.get_res_code(self.expect_respond()) if code != 150: raise InternalError('Server return not 150') result = '' while True: data = self.data_sock.recv(self.BUFF_SIZE) if data is None or len(data) == 0: break result += data.decode() # 接受完毕 self.data_sock.close() self.data_sock = None code = self.get_res_code(self.expect_respond()) if code != 226: raise InternalError('LIST failed!') return result
def change_server_current_root(self, target_dir): # CWD命令,获取server_current_dir assert type( target_dir) == str and target_dir != '', 'given target dir wrong' # 可能要返回上级目录 if target_dir == '..': if self.in_server_root(): raise InternalError('should not have parent dir here') pare, child = os.path.split(self.server_current_dir) if child == '': raise InternalError('child path is none') self.send_msg('CWD ' + pare) else: self.send_msg('CWD ' + target_dir) res = self.expect_respond() code = self.get_res_code(res) if code not in [200, 250]: raise InternalError('Server rejected CWD') # 修改目录 if target_dir == '..': self.server_current_dir = pare else: self.server_current_dir = os.path.join(self.server_current_dir, target_dir) self.console_log( 0, 'server\'s root dir changed to: ' + self.server_current_dir)
def continue_transmit(self): if self.trans_status == self.TRANS_DOWN_STOP or self.trans_status == self.TRANS_UP_STOP: self.send_msg('REST ' + str(self.trans_size)) code = self.get_res_code(self.expect_respond()) if code != 350: raise InternalError('Server rejected REST') return else: raise InternalError('continue but status wrong')
def get_res_code(self, res): assert type(res) == str, 'input res not string' lines = res.split('\n') if len(lines) < 2: raise InternalError('input for res code is empty') code = lines[-2][:3] return int(code)
def login(self, user, psd): assert type(user) == str, 'input user not a string' assert type(psd) == str, 'input password not a string' if user == '' or psd == '': raise LogicError('name and password can\'t be empty.') self.send_msg('USER ' + user) res = self.expect_respond() code = self.get_res_code(res) if code == 230: return elif code in [331, 332]: self.send_msg('PASS ' + psd) res = self.expect_respond() code = self.get_res_code(res) if code == 230: self.status = self.STATUS_LOGGED # 获取一下目录 self.get_server_current_root() self.server_root = self.server_current_dir self.send_msg('TYPE I') code = self.get_res_code(self.expect_respond()) if code != 200: raise InternalError('Server rejected TYPE') return elif code == 202: raise LogicError('permission already granted') elif code == 530: raise LogicError('username or password unacceptable') else: raise LogicError( 'failed to login, see command prompt for infomation') else: raise LogicError('server denied the input user name')
def data_pasv(self): self.send_msg('PASV') res = self.expect_respond() code = self.get_res_code(res) if code != 227: raise InternalError('Server rejected pasv') # 解析服务器返回的字符串 reg = re.compile( '^\S*\s\D*(\d{1,3},\d{1,3},\d{1,3},\d{1,3}),(\d{1,3},\d{1,3}).*$') match_ans = reg.match(res) assert match_ans is not None, 'matching result is none, respond is: ' + res assert len(match_ans.groups() ) == 2, 'match result is wrong: ' + str(match_ans) datacon_ip = match_ans.groups()[0].replace(',', '.') port_ls = match_ans.groups()[1].split(',') assert len(port_ls) == 2, 'port recived error' datacon_port = int(port_ls[0]) * 256 + int(port_ls[1]) assert utility.is_address( datacon_ip, datacon_port), 'recived ip or address format wrong.' # 开始连接 if self.data_sock: self.data_sock.close() self.data_sock = None self.data_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # self.data_sock.connect((datacon_ip, datacon_port)) self.data_sock.connect((self.server_ip, datacon_port)) return
def finish_trans_file(self, is_done, size_done): if is_done: code = self.get_res_code(self.expect_respond()) if code != 226: raise InternalError('failed to transmit') self.trans_size = 0 self.TRANSMITTING = False self.trans_status = self.TRANS_NO_TASK self.console_log(0, 'finished transmit') else: code = self.get_res_code(self.expect_respond()) self.TRANSMITTING = False self.trans_size = size_done if self.trans_status == self.TRANS_DOWN_GOING: self.trans_status = self.TRANS_DOWN_STOP elif self.trans_status == self.TRANS_UP_GOING: self.trans_status = self.TRANS_UP_STOP else: raise InternalError('pause but status wrong') self.console_log(0, 'stopped at size ' + str(self.trans_size))
def get_server_current_root(self): # PWD 命令,获取server_current_dir self.send_msg('PWD') res = self.expect_respond() code = self.get_res_code(res) if code != 257: raise InternalError('Server rejected PWD') dir_re = re.compile('[^"]*"([^"]*)".*') match_ans = dir_re.match(res) assert match_ans is not None, 'matching result is none, respond is:' + res assert len(match_ans.groups() ) == 1, 'match result is wrong: ' + str(match_ans) self.server_current_dir = match_ans.groups()[0] self.console_log(0, 'server\'s root dir is: ' + self.server_current_dir) return
def pause_or_continue(self, *args, **kwargs): if self.session.TRANSMITTING: # 正在传输,意味着要停止 self.session.stop_trans() self.downloading_btn.setText('Continue') else: # 停止了,意味着要继续 self.session.continue_transmit() if self.session.trans_status == self.session.TRANS_DOWN_STOP: self.session.download_file(progress_func=self.change_progress_bar, finish_func=self.finish_transmit) elif self.session.trans_status == self.session.TRANS_UP_STOP: self.session.upload_file(progress_func=self.change_progress_bar, finish_func=self.finish_transmit) else: raise InternalError('wrong in widget') self.downloading_btn.setText('Pause')
def upload_file(self, name='', size=0, progress_func=None, finish_func=None): if self.trans_status == self.TRANS_NO_TASK: # 开启一个新上传任务 self.current_upload['name'] = name self.current_upload['size'] = size self.trans_size = 0 local_file = open(name, 'rb') elif self.trans_status == self.TRANS_UP_STOP: # 继续上传文件 name = self.current_upload['name'] size = self.current_upload['size'] local_file = open(name, 'rb') local_file.seek(self.trans_size) else: return # enter mode if self.trans_mode == self.MODE_PORT: self.data_port() else: self.data_pasv() basename, filename = os.path.split(name) self.send_msg('STOR ' + filename) code = self.get_res_code(self.expect_respond()) if code != 150: raise InternalError('Server rejected STOR, see command for info') # 创建进程开始下载 self.up_thread = uploadThread(self.data_sock, local_file, self.BUFF_SIZE, size, progress_func=progress_func, finish_func=finish_func, have_done=self.trans_size) self.up_thread.start() self.TRANSMITTING = True self.trans_status = self.TRANS_UP_GOING self.console_log(0, 'begin uploading')
def download_file(self, name='', size=0, progress_func=None, finish_func=None): if self.trans_status == self.TRANS_NO_TASK: # 开启一个新下载任务 self.current_download['name'] = name self.current_download['size'] = size self.trans_size = 0 local_file = open(os.path.join(self.root_directory, name), 'wb') elif self.trans_status == self.TRANS_DOWN_STOP: # 继续下载文件 name = self.current_download['name'] size = self.current_download['size'] local_file = open(os.path.join(self.root_directory, name), 'ab+') else: return # enter mode if self.trans_mode == self.MODE_PORT: self.data_port() else: self.data_pasv() self.send_msg('RETR ' + name) code = self.get_res_code(self.expect_respond()) if code != 150: raise InternalError('Server rejected RETR, see command for info') # 创建进程开始下载 self.down_thread = downloadThread(self.data_sock, local_file, self.BUFF_SIZE, size, progress_func=progress_func, finish_func=finish_func, have_done=self.trans_size) self.down_thread.start() self.TRANSMITTING = True self.trans_status = self.TRANS_DOWN_GOING self.console_log(0, 'begin downloading')
def parse_file_info(file_str): ''' 解析形如 -rw-r--r-- 1 1000 121 5689110 Oct 31 13:54 bbb.pdf 格式的字符串 返回json字典,type、size、name字段分别表示:类型,大小,名字 ''' file_re = re.compile('^(\S*)\s*\S*\s*\S*\s*\S*\s*(\S*)\s*\S*\s*\S*\s*\S*\s*(.*)$') match_ans = file_re.match(file_str) if len(match_ans.groups()) < 3: raise InternalError('parse file info accept wrong input: ' + file_str) info = {} if match_ans.groups()[0][0] == 'd': info['type'] = FILE_INFO_DIR else: info['type'] = FILE_INFO_FILE info['size'] = int(match_ans.groups()[1]) info['name'] = match_ans.groups()[2] return info