Beispiel #1
0
 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
Beispiel #2
0
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