def clone_ftp_connection(self): ftp = FTPClient() ftp.data_mode = self.ftp.data_mode host = self.hostEdit.text() port = int(self.portEdit.text()) username = self.userEdit.text() password = self.passwordEdit.text() try: ftp.open_connect_socket(host, port) except: self.log("[Client Error] Network is unreachable.") return None else: ftp.USER(username) ftp.PASS(password) ftp.TYPE() return ftp
class MainWindow(QWidget): COLORS = { '1': 'darkGreen', '2': 'green', '3': 'blue', '4': 'orange', '5': 'red', } def __init__(self, app, parent=None): super(MainWindow, self).__init__(parent) self.connected = False self.app = app self.ftp = FTPClient() self.ftp.responseGet.connect(lambda x: self.log(x)) self.resize(1200, 900) self.setWindowTitle('FTP Client') self.createGui() self.all_buttons = [ self.local.upload_button, self.local.cont_upload_button, self.remote.download_button, self.remote.cont_download_button, self.remote.mkd_button, self.remote.rename_button, self.remote.rmd_button ] for b in self.all_buttons: b.setDisabled(True) # debug # self.hostEdit.setText('59.66.136.21') # self.userEdit.setText('ssast') # self.passwordEdit.setText('ssast') # self.hostEdit.setText('192.168.98.132') # self.userEdit.setText('anonymous') # self.passwordEdit.setText('ssast') # debug end self.connectButton.clicked.connect(self.connectSwitch) self.dataModeButton.clicked.connect(self.dataModeSwitch) self.remote.downloadClicked.connect(self.download) self.remote.contDownloadClicked.connect(self.cont_download) self.local.uploadClicked.connect(self.upload) self.local.contUploadClicked.connect(self.cont_upload) self.local.selectedFileChanged.connect(self.selectedFileChanged) self.remote.selectedFileChanged.connect(self.selectedFileChanged) def log(self, log_str): self.messages.setHidden(True) for l in log_str.strip().split('\n'): if len(l) == 0: continue if l.startswith('[Client Error]'): color = 'darkRed' else: color = self.COLORS.get(l[0], 'black') s = '%s <font color="%s">%s</font>' % (time.strftime( "%Y-%m-%d %H:%M:%S", time.localtime()), color, l) self.messages.append(s) self.messages.setHidden(False) def dataModeSwitch(self): if self.ftp.data_mode == 'PORT': self.ftp.data_mode = 'PASV' elif self.ftp.data_mode == 'PASV': self.ftp.data_mode = 'PORT' else: raise NotImplementedError self.dataModeButton.setText('Use PORT Mode' if self.ftp.data_mode == 'PASV' else 'Use PASV Mode') def connectSwitch(self): if not self.connected: host = self.hostEdit.text() username = self.userEdit.text() password = self.passwordEdit.text() port = int(self.portEdit.text()) try: connect_res = self.ftp.open_connect_socket(host, port) self.log(connect_res) except: self.ftp.close_connect_socket() self.log("[Client Error] Network is unreachable.") else: if connect_res.startswith('220'): user_res = self.ftp.USER(username) self.log(user_res) if user_res.startswith('331'): pass_res = self.ftp.PASS(password) self.log(pass_res) if pass_res.startswith('230'): syst_res = self.ftp.SYST() self.log(syst_res) type_res = self.ftp.TYPE() self.log(type_res) if type_res.startswith('200'): pwd_res = self.ftp.PWD() self.log(pwd_res) cwd = re.search('\".*\"', pwd_res).group()[1:-1] self.connected = True self.remote.cwd = cwd self.remote.updateRemotePath(cwd) self.hostEdit.setDisabled(True) self.userEdit.setDisabled(True) self.passwordEdit.setDisabled(True) self.portEdit.setDisabled(True) if not self.connected: self.log(self.ftp.QUIT()) else: quit_res = self.ftp.QUIT() self.log(quit_res) if quit_res.startswith('221'): self.ftp.close_connect_socket() self.connected = False self.remote.pathEditSetText('') self.remote.files.clear() self.hostEdit.setDisabled(False) self.userEdit.setDisabled(False) self.passwordEdit.setDisabled(False) self.portEdit.setDisabled(False) for b in self.all_buttons: b.setHidden(True) b.setDisabled(True) b.setHidden(False) self.connectButton.setText( 'Connect' if not self.connected else 'Disconnect') def selectedFileChanged(self): def setButtonDisabled(b, disable): b.setHidden(True) b.setDisabled(disable) b.setHidden(False) if self.connected and self.local.selectedIsFile(): setButtonDisabled(self.local.upload_button, False) else: setButtonDisabled(self.local.upload_button, True) if self.connected and self.remote.selectedIsFile(): setButtonDisabled(self.remote.download_button, False) else: setButtonDisabled(self.remote.download_button, True) if self.connected and self.local.selectedIsFile( ) and self.remote.selectedIsFile(): setButtonDisabled(self.local.cont_upload_button, False) setButtonDisabled(self.remote.cont_download_button, False) else: setButtonDisabled(self.local.cont_upload_button, True) setButtonDisabled(self.remote.cont_download_button, True) if self.connected: setButtonDisabled(self.remote.mkd_button, False) else: setButtonDisabled(self.remote.mkd_button, True) if self.connected and self.remote.files.currentItem( ) is not None and self.remote.files.currentItem().text(0) != '..': setButtonDisabled(self.remote.rename_button, False) setButtonDisabled(self.remote.rmd_button, False) else: setButtonDisabled(self.remote.rename_button, True) setButtonDisabled(self.remote.rmd_button, True) # -------------- Data Transimission --------------- def clone_ftp_connection(self): ftp = FTPClient() ftp.data_mode = self.ftp.data_mode host = self.hostEdit.text() port = int(self.portEdit.text()) username = self.userEdit.text() password = self.passwordEdit.text() try: ftp.open_connect_socket(host, port) except: self.log("[Client Error] Network is unreachable.") return None else: ftp.USER(username) ftp.PASS(password) ftp.TYPE() return ftp def download(self): if self.remote.files.currentItem() is None: return filename = self.remote.files.currentItem().text(0) filesize = int(self.remote.files.currentItem().text(1)) remote_dir = self.remote.cwd local_dir = self.local.cwd remote_file = os.path.join(remote_dir, filename) local_file = os.path.join(local_dir, filename) d_ftp = self.clone_ftp_connection() if d_ftp is None: return item = QTreeWidgetItem() item.setText(0, 'remote -> local') item.setText(1, local_file) item.setText(2, remote_file) item.setText(3, str(filesize)) item.setText(4, '0%') item.setText(5, 'Pending') self.queue.addTopLevelItem(item) downloadThread = DownloadThread(d_ftp, remote_file, local_file, self) downloadThread.responseGet.connect(lambda x: self.log(x)) def modify_item(x): if filesize != 0: item.setText(4, ('%.2f' % (x / filesize * 100)) + '%') else: item.setText(4, '100%') item.setText(5, 'Downloading') downloadThread.dataProgress.connect(modify_item) def downloadDone(): item.setText(5, 'Done') self.local.updateLocalPath(self.local.cwd) downloadThread.finish.connect(downloadDone) def downloadFailed(): item.setText(5, 'Failed') self.local.updateLocalPath(self.local.cwd) downloadThread.failed.connect(downloadFailed) downloadThread.start() def cont_download(self): if self.remote.files.currentItem( ) is None or self.local.files.currentItem() is None: return if self.remote.files.currentItem().text( 0) != self.local.files.currentItem().text(0): if not confirmDialog('Continue to Download', 'Filename not Same. Sure?', parent=self): return filename = self.remote.files.currentItem().text(0) filesize = int(self.remote.files.currentItem().text(1)) rest = int(self.local.files.currentItem().text(1)) remote_dir = self.remote.cwd local_dir = self.local.cwd remote_file = os.path.join(remote_dir, filename) local_file = os.path.join(local_dir, self.local.files.currentItem().text(0)) d_ftp = self.clone_ftp_connection() if d_ftp is None: return item = QTreeWidgetItem() item.setText(0, 'remote -> local') item.setText(1, local_file) item.setText(2, remote_file) item.setText(3, str(filesize)) item.setText(4, ('%.2f' % (rest / filesize * 100)) + '%') item.setText(5, 'Pending') self.queue.addTopLevelItem(item) downloadThread = DownloadThread(d_ftp, remote_file, local_file, self, rest) downloadThread.responseGet.connect(lambda x: self.log(x)) def modify_item(x): if filesize != 0: item.setText(4, ('%.2f' % ((x + rest) / filesize * 100)) + '%') else: item.setText(4, '100%') item.setText(5, 'Downloading') downloadThread.dataProgress.connect(modify_item) def downloadDone(): item.setText(5, 'Done') self.local.updateLocalPath(self.local.cwd) downloadThread.finish.connect(downloadDone) def downloadFailed(): item.setText(5, 'Failed') self.local.updateLocalPath(self.local.cwd) downloadThread.failed.connect(downloadFailed) downloadThread.start() def upload(self): if self.local.files.currentItem() is None: return filename = self.local.files.currentItem().text(0) filesize = int(self.local.files.currentItem().text(1)) remote_dir = self.remote.cwd local_dir = self.local.cwd remote_file = os.path.join(remote_dir, filename) local_file = os.path.join(local_dir, filename) u_ftp = self.clone_ftp_connection() if u_ftp is None: return item = QTreeWidgetItem() item.setText(0, 'local -> remote') item.setText(1, local_file) item.setText(2, remote_file) item.setText(3, str(filesize)) item.setText(4, '0%') item.setText(5, 'Pending') self.queue.addTopLevelItem(item) uploadThread = UploadThread(u_ftp, remote_file, local_file, self) uploadThread.responseGet.connect(lambda x: self.log(x)) def modify_item(x): if filesize != 0: item.setText(4, ('%.2f' % (x / filesize * 100)) + '%') else: item.setText(4, '100%') item.setText(5, 'Uploading') uploadThread.dataProgress.connect(modify_item) def uploadDone(): item.setText(5, 'Done') self.remote.updateRemotePath(self.remote.cwd) uploadThread.finish.connect(uploadDone) def uploadFailed(): item.setText(5, 'Failed') self.remote.updateRemotePath(self.remote.cwd) uploadThread.failed.connect(uploadFailed) uploadThread.start() def cont_upload(self): if self.remote.files.currentItem( ) is None or self.local.files.currentItem() is None: return if self.remote.files.currentItem().text( 0) != self.local.files.currentItem().text(0): if not confirmDialog('Continue to Upload', 'Filename not Same. Sure?', parent=self): return filename = self.local.files.currentItem().text(0) filesize = int(self.local.files.currentItem().text(1)) rest = int(self.remote.files.currentItem().text(1)) remote_dir = self.remote.cwd local_dir = self.local.cwd remote_file = os.path.join(remote_dir, self.remote.files.currentItem().text(0)) local_file = os.path.join(local_dir, filename) u_ftp = self.clone_ftp_connection() if u_ftp is None: return item = QTreeWidgetItem() item.setText(0, 'local -> remote') item.setText(1, local_file) item.setText(2, remote_file) item.setText(3, str(filesize)) item.setText(4, ('%.2f' % (rest / filesize * 100)) + '%') item.setText(5, 'Pending') self.queue.addTopLevelItem(item) uploadThread = UploadThread(u_ftp, remote_file, local_file, self, rest) uploadThread.responseGet.connect(lambda x: self.log(x)) def modify_item(x): if filesize != 0: item.setText(4, ('%.2f' % ((x + rest) / filesize * 100)) + '%') else: item.setText(4, '100%') item.setText(5, 'Uploading') uploadThread.dataProgress.connect(modify_item) def uploadDone(): item.setText(5, 'Done') self.remote.updateRemotePath(self.remote.cwd) uploadThread.finish.connect(uploadDone) def uploadFailed(): item.setText(5, 'Failed') self.remote.updateRemotePath(self.remote.cwd) uploadThread.failed.connect(uploadFailed) uploadThread.start() # --------------------- GUI ----------------------- def createGui(self): self.mainLayout = QtWidgets.QVBoxLayout() self.mainLayout.addLayout(self.createLoginLayout()) self.mainLayout.addLayout(self.createMessageLayout(), stretch=1) self.mainLayout.addLayout(self.createFileLayout(), stretch=4) self.mainLayout.addLayout(self.createQueueLayout(), stretch=1) self.setLayout(self.mainLayout) def createLoginLayout(self): loginLayout = QHBoxLayout() hostLabel = QLabel('Host') self.hostEdit = QLineEdit() loginLayout.addWidget(hostLabel) loginLayout.addWidget(self.hostEdit) userLabel = QLabel('User') self.userEdit = QLineEdit() # self.userEdit.setText('anonymous') # self.userEdit.setDisabled(True) loginLayout.addWidget(userLabel) loginLayout.addWidget(self.userEdit) passwordLabel = QLabel('Password') self.passwordEdit = QLineEdit() self.passwordEdit.setEchoMode(QLineEdit.Password) loginLayout.addWidget(passwordLabel) loginLayout.addWidget(self.passwordEdit) portLabel = QLabel('Port') self.portEdit = QLineEdit('21') loginLayout.addWidget(portLabel) loginLayout.addWidget(self.portEdit) self.connectButton = QPushButton() self.connectButton.setText( 'Connect' if not self.connected else 'Disconnect') loginLayout.addWidget(self.connectButton) self.dataModeButton = QPushButton() self.dataModeButton.setText('Use PORT Mode' if self.ftp.data_mode == 'PASV' else 'Use PASV Mode') loginLayout.addWidget(self.dataModeButton) return loginLayout def createMessageLayout(self): messageLayout = QVBoxLayout() self.messages = QTextBrowser() messageLayout.addWidget(self.messages) return messageLayout def createFileLayout(self): fileLayout = QHBoxLayout() self.local = LocalFileWidget(parent=self) self.remote = RemoteFileWidget(self.ftp, parent=self) fileLayout.addWidget(self.local) fileLayout.addWidget(self.remote) return fileLayout def createQueueLayout(self): queueLayout = QHBoxLayout() self.queue = QTreeWidget() self.queue.setRootIsDecorated(False) self.queue.setHeaderLabels([ 'Direction', 'Local File', 'Remote File', 'Size', 'Progress', 'Status' ]) queueLayout.addWidget(self.queue) return queueLayout