示例#1
0
    def __init__(self, url, accessHandler, parent=None):
        """
        Constructor
        
        @param url requested FTP URL (QUrl)
        @param accessHandler reference to the access handler (FtpAccessHandler)
        @param parent reference to the parent object (QObject)
        """
        super(FtpReply, self).__init__(parent)

        self.__manager = parent
        self.__handler = accessHandler

        self.__ftp = E5Ftp()

        self.__items = []
        self.__content = QByteArray()
        self.__units = [
            "Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"
        ]
        self.__dirLineParser = FtpDirLineParser()
        self.__fileBytesReceived = 0

        if url.path() == "":
            url.setPath("/")
        self.setUrl(url)

        # do proxy setup
        if not Preferences.getUI("UseProxy"):
            proxyType = E5FtpProxyType.NoProxy
        else:
            proxyType = Preferences.getUI("ProxyType/Ftp")
        if proxyType != E5FtpProxyType.NoProxy:
            self.__ftp.setProxy(proxyType, Preferences.getUI("ProxyHost/Ftp"),
                                Preferences.getUI("ProxyPort/Ftp"))
            if proxyType != E5FtpProxyType.NonAuthorizing:
                self.__ftp.setProxyAuthentication(
                    Preferences.getUI("ProxyUser/Ftp"),
                    Preferences.getUI("ProxyPassword/Ftp"),
                    Preferences.getUI("ProxyAccount/Ftp"))

        QTimer.singleShot(0, self.__doFtpCommands)
示例#2
0
    def initialLoadAndCheck(self, forceUpload):
        """
        Public method to do the initial check.
        
        @keyparam forceUpload flag indicating a forced upload of the files
            (boolean)
        """
        if not Preferences.getHelp("SyncEnabled"):
            return

        self.__state = "initializing"
        self.__forceUpload = forceUpload

        self.__dirLineParser = FtpDirLineParser()
        self.__remoteFilesFound = {}

        self.__idleTimer = QTimer(self)
        self.__idleTimer.setInterval(
            Preferences.getHelp("SyncFtpIdleTimeout") * 1000)
        self.__idleTimer.timeout.connect(self.__idleTimeout)

        self.__ftp = E5Ftp()

        # do proxy setup
        if not Preferences.getUI("UseProxy"):
            proxyType = E5FtpProxyType.NoProxy
        else:
            proxyType = Preferences.getUI("ProxyType/Ftp")
        if proxyType != E5FtpProxyType.NoProxy:
            self.__ftp.setProxy(proxyType, Preferences.getUI("ProxyHost/Ftp"),
                                Preferences.getUI("ProxyPort/Ftp"))
            if proxyType != E5FtpProxyType.NonAuthorizing:
                self.__ftp.setProxyAuthentication(
                    Preferences.getUI("ProxyUser/Ftp"),
                    Preferences.getUI("ProxyPassword/Ftp"),
                    Preferences.getUI("ProxyAccount/Ftp"))

        QTimer.singleShot(0, self.__doFtpCommands)
示例#3
0
 def __init__(self, url, accessHandler, parent=None):
     """
     Constructor
     
     @param url requested FTP URL (QUrl)
     @param accessHandler reference to the access handler (FtpAccessHandler)
     @param parent reference to the parent object (QObject)
     """
     super(FtpReply, self).__init__(parent)
     
     self.__manager = parent
     self.__handler = accessHandler
     
     self.__ftp = E5Ftp()
     
     self.__items = []
     self.__content = QByteArray()
     self.__units = ["Bytes", "KB", "MB", "GB", "TB",
                     "PB", "EB", "ZB", "YB"]
     self.__dirLineParser = FtpDirLineParser()
     self.__fileBytesReceived = 0
     
     if url.path() == "":
         url.setPath("/")
     self.setUrl(url)
     
     # do proxy setup
     if not Preferences.getUI("UseProxy"):
         proxyType = E5FtpProxyType.NoProxy
     else:
         proxyType = Preferences.getUI("ProxyType/Ftp")
     if proxyType != E5FtpProxyType.NoProxy:
         self.__ftp.setProxy(
             proxyType,
             Preferences.getUI("ProxyHost/Ftp"),
             Preferences.getUI("ProxyPort/Ftp"))
         if proxyType != E5FtpProxyType.NonAuthorizing:
             self.__ftp.setProxyAuthentication(
                 Preferences.getUI("ProxyUser/Ftp"),
                 Preferences.getUI("ProxyPassword/Ftp"),
                 Preferences.getUI("ProxyAccount/Ftp"))
     
     QTimer.singleShot(0, self.__doFtpCommands)
示例#4
0
 def initialLoadAndCheck(self, forceUpload):
     """
     Public method to do the initial check.
     
     @keyparam forceUpload flag indicating a forced upload of the files
         (boolean)
     """
     if not Preferences.getHelp("SyncEnabled"):
         return
     
     self.__state = "initializing"
     self.__forceUpload = forceUpload
     
     self.__dirLineParser = FtpDirLineParser()
     self.__remoteFilesFound = {}
     
     self.__idleTimer = QTimer(self)
     self.__idleTimer.setInterval(
         Preferences.getHelp("SyncFtpIdleTimeout") * 1000)
     self.__idleTimer.timeout.connect(self.__idleTimeout)
     
     self.__ftp = E5Ftp()
     
     # do proxy setup
     if not Preferences.getUI("UseProxy"):
         proxyType = E5FtpProxyType.NoProxy
     else:
         proxyType = Preferences.getUI("ProxyType/Ftp")
     if proxyType != E5FtpProxyType.NoProxy:
         self.__ftp.setProxy(
             proxyType,
             Preferences.getUI("ProxyHost/Ftp"),
             Preferences.getUI("ProxyPort/Ftp"))
         if proxyType != E5FtpProxyType.NonAuthorizing:
             self.__ftp.setProxyAuthentication(
                 Preferences.getUI("ProxyUser/Ftp"),
                 Preferences.getUI("ProxyPassword/Ftp"),
                 Preferences.getUI("ProxyAccount/Ftp"))
     
     QTimer.singleShot(0, self.__doFtpCommands)
示例#5
0
class FtpReply(QNetworkReply):
    """
    Class implementing a network reply for FTP resources.
    """
    def __init__(self, url, accessHandler, parent=None):
        """
        Constructor
        
        @param url requested FTP URL (QUrl)
        @param accessHandler reference to the access handler (FtpAccessHandler)
        @param parent reference to the parent object (QObject)
        """
        super(FtpReply, self).__init__(parent)

        self.__manager = parent
        self.__handler = accessHandler

        self.__ftp = E5Ftp()

        self.__items = []
        self.__content = QByteArray()
        self.__units = [
            "Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"
        ]
        self.__dirLineParser = FtpDirLineParser()
        self.__fileBytesReceived = 0

        if url.path() == "":
            url.setPath("/")
        self.setUrl(url)

        # do proxy setup
        if not Preferences.getUI("UseProxy"):
            proxyType = E5FtpProxyType.NoProxy
        else:
            proxyType = Preferences.getUI("ProxyType/Ftp")
        if proxyType != E5FtpProxyType.NoProxy:
            self.__ftp.setProxy(proxyType, Preferences.getUI("ProxyHost/Ftp"),
                                Preferences.getUI("ProxyPort/Ftp"))
            if proxyType != E5FtpProxyType.NonAuthorizing:
                self.__ftp.setProxyAuthentication(
                    Preferences.getUI("ProxyUser/Ftp"),
                    Preferences.getUI("ProxyPassword/Ftp"),
                    Preferences.getUI("ProxyAccount/Ftp"))

        QTimer.singleShot(0, self.__doFtpCommands)

    def abort(self):
        """
        Public slot to abort the operation.
        """
        # do nothing
        pass

    def bytesAvailable(self):
        """
        Public method to determined the bytes available for being read.
        
        @return bytes available (integer)
        """
        return self.__content.size()

    def isSequential(self):
        """
        Public method to check for sequential access.
        
        @return flag indicating sequential access (boolean)
        """
        return True

    def readData(self, maxlen):
        """
        Public method to retrieve data from the reply object.
        
        @param maxlen maximum number of bytes to read (integer)
        @return string containing the data (bytes)
        """
        if self.__content.size():
            len_ = min(maxlen, self.__content.size())
            buffer = bytes(self.__content[:len_])
            self.__content.remove(0, len_)
            return buffer

    def __doFtpCommands(self):
        """
        Private slot doing the sequence of FTP commands to get the requested
        result.
        """
        retry = True
        try:
            username = self.url().userName()
            password = self.url().password()
            byAuth = False

            while retry:
                try:
                    self.__ftp.connect(self.url().host(),
                                       self.url().port(ftplib.FTP_PORT),
                                       timeout=10)
                except E5FtpProxyError as err:
                    self.setError(QNetworkReply.ProxyNotFoundError, str(err))
                    self.error.emit(QNetworkReply.ProxyNotFoundError)
                    self.finished.emit()
                ok, retry = self.__doFtpLogin(username, password, byAuth)
                if not ok and retry:
                    auth = self.__handler.getAuthenticator(self.url().host())
                    if auth and not auth.isNull() and auth.user():
                        username = auth.user()
                        password = auth.password()
                        byAuth = True
                    else:
                        retry = False
            if ok:
                self.__ftp.retrlines("LIST " + self.url().path(),
                                     self.__dirCallback)
                if len(self.__items) == 1 and \
                   self.__items[0].isFile():
                    self.__fileBytesReceived = 0
                    self.__setContent()
                    self.__ftp.retrbinary("RETR " + self.url().path(),
                                          self.__retrCallback)
                    self.__content.append(512 * b' ')
                    self.readyRead.emit()
                else:
                    self.__setListContent()
                self.__ftp.quit()
        except ftplib.all_errors as err:
            if isinstance(err, socket.gaierror):
                errCode = QNetworkReply.HostNotFoundError
            elif isinstance(err, socket.error) and \
                    err.errno == errno.ECONNREFUSED:
                errCode = QNetworkReply.ConnectionRefusedError
            else:
                errCode = QNetworkReply.ProtocolFailure
            self.setError(errCode, str(err))
            self.error.emit(errCode)
        self.finished.emit()

    def __doFtpLogin(self, username, password, byAuth=False):
        """
        Private method to do the FTP login with asking for a username and
        password, if the login fails with an error 530.
        
        @param username user name to use for the login (string)
        @param password password to use for the login (string)
        @param byAuth flag indicating that the login data was provided by an
            authenticator (boolean)
        @return tuple of two flags indicating a successful login and
            if the login should be retried (boolean, boolean)
        """
        try:
            self.__ftp.login(username, password)
            return True, False
        except E5FtpProxyError as err:
            code = str(err)[:3]
            if code[1] == "5":
                # could be a 530, check second line
                lines = str(err).splitlines()
                if lines[1][:3] == "530":
                    if "usage" in "\n".join(lines[1:].lower()):
                        # found a not supported proxy
                        self.setError(
                            QNetworkReply.ProxyConnectionRefusedError,
                            self.tr("The proxy type seems to be wrong."
                                    " If it is not in the list of"
                                    " supported proxy types please report"
                                    " it with the instructions given by"
                                    " the proxy.\n{0}").format("\n".join(
                                        lines[1:])))
                        self.error.emit(
                            QNetworkReply.ProxyConnectionRefusedError)
                        return False, False
                    else:
                        from UI.AuthenticationDialog import \
                            AuthenticationDialog
                        info = self.tr(
                            "<b>Connect to proxy '{0}' using:</b>")\
                            .format(Utilities.html_encode(
                                Preferences.getUI("ProxyHost/Ftp")))
                        dlg = AuthenticationDialog(
                            info, Preferences.getUI("ProxyUser/Ftp"), True)
                        dlg.setData(Preferences.getUI("ProxyUser/Ftp"),
                                    Preferences.getUI("ProxyPassword/Ftp"))
                        if dlg.exec_() == QDialog.Accepted:
                            username, password = dlg.getData()
                            if dlg.shallSave():
                                Preferences.setUI("ProxyUser/Ftp", username)
                                Preferences.setUI("ProxyPassword/Ftp",
                                                  password)
                            self.__ftp.setProxyAuthentication(
                                username, password)
                            return False, True
            return False, False
        except (ftplib.error_perm, ftplib.error_temp) as err:
            code = err.args[0].strip()[:3]
            if code in ["530", "421"]:
                # error 530 -> Login incorrect
                # error 421 -> Login may be incorrect (reported by some
                # proxies)
                if byAuth:
                    self.__handler.setAuthenticator(self.url().host(), None)
                    auth = None
                else:
                    auth = self.__handler.getAuthenticator(self.url().host())
                if not auth or auth.isNull() or not auth.user():
                    auth = QAuthenticator()
                    self.__manager.authenticationRequired.emit(self, auth)
                    if not auth.isNull():
                        if auth.user():
                            self.__handler.setAuthenticator(
                                self.url().host(), auth)
                            return False, True
                    return False, False
                return False, True
            else:
                raise

    def __dirCallback(self, line):
        """
        Private slot handling the receipt of directory listings.
        
        @param line the received line of the directory listing (string)
        """
        try:
            urlInfo = self.__dirLineParser.parseLine(line)
        except FtpDirLineParserError:
            # silently ignore parser errors
            urlInfo = None

        if urlInfo:
            self.__items.append(urlInfo)

        QCoreApplication.processEvents()

    def __retrCallback(self, data):
        """
        Private slot handling the reception of data.
        
        @param data data received from the FTP server (bytes)
        """
        self.__content += QByteArray(data)
        self.__fileBytesReceived += len(data)
        self.downloadProgress.emit(self.__fileBytesReceived,
                                   self.__items[0].size())
        self.readyRead.emit()

        QCoreApplication.processEvents()

    def __setContent(self):
        """
        Private method to finish the setup of the data.
        """
        mtype, encoding = mimetypes.guess_type(self.url().toString())
        self.open(QIODevice.ReadOnly | QIODevice.Unbuffered)
        self.setHeader(QNetworkRequest.ContentLengthHeader,
                       self.__items[0].size())
        if mtype:
            self.setHeader(QNetworkRequest.ContentTypeHeader, mtype)
        self.setAttribute(QNetworkRequest.HttpStatusCodeAttribute, 200)
        self.setAttribute(QNetworkRequest.HttpReasonPhraseAttribute, "Ok")
        self.metaDataChanged.emit()

    def __cssLinkClass(self, icon, size=32):
        """
        Private method to generate a link class with an icon.
        
        @param icon icon to be included (QIcon)
        @param size size of the icon to be generated (integer)
        @return CSS class string (string)
        """
        cssString = \
            """a.{{0}} {{{{\n"""\
            """  padding-left: {0}px;\n"""\
            """  background: transparent url(data:image/png;base64,{1})"""\
            """ no-repeat center left;\n"""\
            """  font-weight: bold;\n"""\
            """}}}}\n"""
        pixmap = icon.pixmap(size, size)
        imageBuffer = QBuffer()
        imageBuffer.open(QIODevice.ReadWrite)
        if not pixmap.save(imageBuffer, "PNG"):
            # write a blank pixmap on error
            pixmap = QPixmap(size, size)
            pixmap.fill(Qt.transparent)
            imageBuffer.buffer().clear()
            pixmap.save(imageBuffer, "PNG")
        return cssString.format(
            size + 4, str(imageBuffer.buffer().toBase64(), encoding="ascii"))

    def __setListContent(self):
        """
        Private method to prepare the content for the reader.
        """
        u = self.url()
        if not u.path().endswith("/"):
            u.setPath(u.path() + "/")

        baseUrl = self.url().toString()
        basePath = u.path()

        linkClasses = {}
        iconSize = QWebSettings.globalSettings().fontSize(
            QWebSettings.DefaultFontSize)

        parent = u.resolved(QUrl(".."))
        if parent.isParentOf(u):
            icon = UI.PixmapCache.getIcon("up.png")
            linkClasses["link_parent"] = \
                self.__cssLinkClass(icon, iconSize).format("link_parent")
            parentStr = self.tr(
                """  <p><a class="link_parent" href="{0}">"""
                """Change to parent directory</a></p>""").format(
                    parent.toString())
        else:
            parentStr = ""

        row = \
            """    <tr class="{0}">"""\
            """<td class="name"><a class="{1}" href="{2}">{3}</a></td>"""\
            """<td class="size">{4}</td>"""\
            """<td class="modified">{5}</td>"""\
            """</tr>\n"""
        table = self.tr("""    <tr>"""
                        """<th align="left">Name</th>"""
                        """<th>Size</th>"""
                        """<th align="left">Last modified</th>"""
                        """</tr>\n""")

        i = 0
        for item in self.__items:
            name = item.name()
            if item.isDir() and not name.endswith("/"):
                name += "/"
            child = u.resolved(QUrl(name.replace(":", "%3A")))

            if item.isFile():
                size = item.size()
                unit = 0
                while size:
                    newSize = size // 1024
                    if newSize and unit < len(self.__units):
                        size = newSize
                        unit += 1
                    else:
                        break

                sizeStr = self.tr("{0} {1}", "size unit")\
                    .format(size, self.__units[unit])
                linkClass = "link_file"
                if linkClass not in linkClasses:
                    icon = UI.PixmapCache.getIcon("fileMisc.png")
                    linkClasses[linkClass] = \
                        self.__cssLinkClass(icon, iconSize).format(linkClass)
            else:
                sizeStr = ""
                linkClass = "link_dir"
                if linkClass not in linkClasses:
                    icon = UI.PixmapCache.getIcon("dirClosed.png")
                    linkClasses[linkClass] = \
                        self.__cssLinkClass(icon, iconSize).format(linkClass)
            table += row.format(
                i == 0 and "odd" or "even",
                linkClass,
                child.toString(),
                Utilities.html_encode(item.name()),
                sizeStr,
                item.lastModified().toString("yyyy-MM-dd hh:mm"),
            )
            i = 1 - i

        content = ftpListPage_html.format(
            Utilities.html_encode(baseUrl), "".join(linkClasses.values()),
            self.tr("Listing of {0}").format(basePath), parentStr, table)
        self.__content = QByteArray(content.encode("utf8"))
        self.__content.append(512 * b' ')

        self.open(QIODevice.ReadOnly | QIODevice.Unbuffered)
        self.setHeader(QNetworkRequest.ContentTypeHeader,
                       "text/html; charset=UTF-8")
        self.setHeader(QNetworkRequest.ContentLengthHeader,
                       self.__content.size())
        self.setAttribute(QNetworkRequest.HttpStatusCodeAttribute, 200)
        self.setAttribute(QNetworkRequest.HttpReasonPhraseAttribute, "Ok")
        self.metaDataChanged.emit()
        self.downloadProgress.emit(self.__content.size(),
                                   self.__content.size())
        self.readyRead.emit()
示例#6
0
class FtpSyncHandler(SyncHandler):
    """
    Class implementing a synchronization handler using FTP.
    
    @signal syncStatus(type_, message) emitted to indicate the synchronization
        status (string one of "bookmarks", "history", "passwords",
        "useragents" or "speeddial", string)
    @signal syncError(message) emitted for a general error with the error
        message (string)
    @signal syncMessage(message) emitted to send a message about
        synchronization (string)
    @signal syncFinished(type_, done, download) emitted after a
        synchronization has finished (string one of "bookmarks", "history",
        "passwords", "useragents" or "speeddial", boolean, boolean)
    """
    syncStatus = pyqtSignal(str, str)
    syncError = pyqtSignal(str)
    syncMessage = pyqtSignal(str)
    syncFinished = pyqtSignal(str, bool, bool)

    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        super(FtpSyncHandler, self).__init__(parent)

        self.__state = "idle"
        self.__forceUpload = False
        self.__connected = False

        self.__remoteFilesFound = {}

    def initialLoadAndCheck(self, forceUpload):
        """
        Public method to do the initial check.
        
        @keyparam forceUpload flag indicating a forced upload of the files
            (boolean)
        """
        if not Preferences.getHelp("SyncEnabled"):
            return

        self.__state = "initializing"
        self.__forceUpload = forceUpload

        self.__dirLineParser = FtpDirLineParser()
        self.__remoteFilesFound = {}

        self.__idleTimer = QTimer(self)
        self.__idleTimer.setInterval(
            Preferences.getHelp("SyncFtpIdleTimeout") * 1000)
        self.__idleTimer.timeout.connect(self.__idleTimeout)

        self.__ftp = E5Ftp()

        # do proxy setup
        if not Preferences.getUI("UseProxy"):
            proxyType = E5FtpProxyType.NoProxy
        else:
            proxyType = Preferences.getUI("ProxyType/Ftp")
        if proxyType != E5FtpProxyType.NoProxy:
            self.__ftp.setProxy(proxyType, Preferences.getUI("ProxyHost/Ftp"),
                                Preferences.getUI("ProxyPort/Ftp"))
            if proxyType != E5FtpProxyType.NonAuthorizing:
                self.__ftp.setProxyAuthentication(
                    Preferences.getUI("ProxyUser/Ftp"),
                    Preferences.getUI("ProxyPassword/Ftp"),
                    Preferences.getUI("ProxyAccount/Ftp"))

        QTimer.singleShot(0, self.__doFtpCommands)

    def __doFtpCommands(self):
        """
        Private slot executing the sequence of FTP commands.
        """
        try:
            ok = self.__connectAndLogin()
            if ok:
                self.__changeToStore()
                self.__ftp.retrlines("LIST", self.__dirListCallback)
                self.__initialSync()
                self.__state = "idle"
                self.__idleTimer.start()
        except (ftplib.all_errors + (E5FtpProxyError, )) as err:
            self.syncError.emit(str(err))

    def __connectAndLogin(self):
        """
        Private method to connect to the FTP server and log in.
        
        @return flag indicating a successful log in (boolean)
        """
        self.__ftp.connect(Preferences.getHelp("SyncFtpServer"),
                           Preferences.getHelp("SyncFtpPort"),
                           timeout=5)
        self.__ftp.login(Preferences.getHelp("SyncFtpUser"),
                         Preferences.getHelp("SyncFtpPassword"))
        self.__connected = True
        return True

    def __changeToStore(self):
        """
        Private slot to change to the storage directory.
        
        This action will create the storage path on the server, if it
        does not exist. Upon return, the current directory of the server
        is the sync directory.
        """
        storePathList = \
            Preferences.getHelp("SyncFtpPath").replace("\\", "/").split("/")
        if storePathList[0] == "":
            storePathList.pop(0)
        while storePathList:
            path = storePathList[0]
            try:
                self.__ftp.cwd(path)
            except ftplib.error_perm as err:
                code = err.args[0].strip()[:3]
                if code == "550":
                    # path does not exist, create it
                    self.__ftp.mkd(path)
                    self.__ftp.cwd(path)
                else:
                    raise
            storePathList.pop(0)

    def __dirListCallback(self, line):
        """
        Private slot handling the receipt of directory listing lines.
        
        @param line the received line of the directory listing (string)
        """
        try:
            urlInfo = self.__dirLineParser.parseLine(line)
        except FtpDirLineParserError:
            # silently ignore parser errors
            urlInfo = None

        if urlInfo and urlInfo.isValid() and urlInfo.isFile():
            if urlInfo.name() in self._remoteFiles.values():
                self.__remoteFilesFound[urlInfo.name()] = \
                    urlInfo.lastModified()

        QCoreApplication.processEvents()

    def __downloadFile(self, type_, fileName, timestamp):
        """
        Private method to downlaod the given file.
        
        @param type_ type of the synchronization event (string one
            of "bookmarks", "history", "passwords", "useragents" or
            "speeddial")
        @param fileName name of the file to be downloaded (string)
        @param timestamp time stamp in seconds of the file to be downloaded
            (integer)
        """
        self.syncStatus.emit(type_, self._messages[type_]["RemoteExists"])
        buffer = io.BytesIO()
        try:
            self.__ftp.retrbinary(
                "RETR {0}".format(self._remoteFiles[type_]),
                lambda x: self.__downloadFileCallback(buffer, x))
            ok, error = self.writeFile(QByteArray(buffer.getvalue()), fileName,
                                       type_, timestamp)
            if not ok:
                self.syncStatus.emit(type_, error)
            self.syncFinished.emit(type_, ok, True)
        except ftplib.all_errors as err:
            self.syncStatus.emit(type_, str(err))
            self.syncFinished.emit(type_, False, True)

    def __downloadFileCallback(self, buffer, data):
        """
        Private method receiving the downloaded data.
        
        @param buffer reference to the buffer (io.BytesIO)
        @param data byte string to store in the buffer (bytes)
        @return number of bytes written to the buffer (integer)
        """
        res = buffer.write(data)
        QCoreApplication.processEvents()
        return res

    def __uploadFile(self, type_, fileName):
        """
        Private method to upload the given file.
        
        @param type_ type of the synchronization event (string one
            of "bookmarks", "history", "passwords", "useragents" or
            "speeddial")
        @param fileName name of the file to be uploaded (string)
        @return flag indicating success (boolean)
        """
        res = False
        data = self.readFile(fileName, type_)
        if data.isEmpty():
            self.syncStatus.emit(type_, self._messages[type_]["LocalMissing"])
            self.syncFinished.emit(type_, False, False)
        else:
            buffer = io.BytesIO(data.data())
            try:
                self.__ftp.storbinary(
                    "STOR {0}".format(self._remoteFiles[type_]),
                    buffer,
                    callback=lambda x: QCoreApplication.processEvents())
                self.syncFinished.emit(type_, True, False)
                res = True
            except ftplib.all_errors as err:
                self.syncStatus.emit(type_, str(err))
                self.syncFinished.emit(type_, False, False)
        return res

    def __initialSyncFile(self, type_, fileName):
        """
        Private method to do the initial synchronization of the given file.
        
        @param type_ type of the synchronization event (string one
            of "bookmarks", "history", "passwords", "useragents" or
            "speeddial")
        @param fileName name of the file to be synchronized (string)
        """
        if not self.__forceUpload and \
           self._remoteFiles[type_] in self.__remoteFilesFound:
            if QFileInfo(fileName).lastModified() < \
               self.__remoteFilesFound[self._remoteFiles[type_]]:
                self.__downloadFile(
                    type_, fileName, self.__remoteFilesFound[
                        self._remoteFiles[type_]].toTime_t())
            else:
                self.syncStatus.emit(type_,
                                     self.tr("No synchronization required."))
                self.syncFinished.emit(type_, True, True)
        else:
            if self._remoteFiles[type_] not in self.__remoteFilesFound:
                self.syncStatus.emit(type_,
                                     self._messages[type_]["RemoteMissing"])
            else:
                self.syncStatus.emit(type_,
                                     self._messages[type_]["LocalNewer"])
            self.__uploadFile(type_, fileName)

    def __initialSync(self):
        """
        Private slot to do the initial synchronization.
        """
        # Bookmarks
        if Preferences.getHelp("SyncBookmarks"):
            self.__initialSyncFile(
                "bookmarks",
                Helpviewer.HelpWindow.HelpWindow.bookmarksManager().
                getFileName())

        # History
        if Preferences.getHelp("SyncHistory"):
            self.__initialSyncFile(
                "history",
                Helpviewer.HelpWindow.HelpWindow.historyManager().getFileName(
                ))

        # Passwords
        if Preferences.getHelp("SyncPasswords"):
            self.__initialSyncFile(
                "passwords",
                Helpviewer.HelpWindow.HelpWindow.passwordManager().getFileName(
                ))

        # User Agent Settings
        if Preferences.getHelp("SyncUserAgents"):
            self.__initialSyncFile(
                "useragents",
                Helpviewer.HelpWindow.HelpWindow.userAgentsManager().
                getFileName())

        # Speed Dial Settings
        if Preferences.getHelp("SyncSpeedDial"):
            self.__initialSyncFile(
                "speeddial",
                Helpviewer.HelpWindow.HelpWindow.speedDial().getFileName())

        self.__forceUpload = False

    def __syncFile(self, type_, fileName):
        """
        Private method to synchronize the given file.
        
        @param type_ type of the synchronization event (string one
            of "bookmarks", "history", "passwords", "useragents" or
            "speeddial")
        @param fileName name of the file to be synchronized (string)
        """
        if self.__state == "initializing":
            return

        # use idle timeout to check, if we are still connected
        if self.__connected:
            self.__idleTimeout()
        if not self.__connected or self.__ftp.sock is None:
            ok = self.__connectAndLogin()
            if not ok:
                self.syncStatus.emit(type_,
                                     self.tr("Cannot log in to FTP host."))
                return

        # upload the changed file
        self.__state = "uploading"
        self.syncStatus.emit(type_, self._messages[type_]["Uploading"])
        if self.__uploadFile(type_, fileName):
            self.syncStatus.emit(type_, self.tr("Synchronization finished."))
        self.__state = "idle"

    def syncBookmarks(self):
        """
        Public method to synchronize the bookmarks.
        """
        self.__syncFile(
            "bookmarks",
            Helpviewer.HelpWindow.HelpWindow.bookmarksManager().getFileName())

    def syncHistory(self):
        """
        Public method to synchronize the history.
        """
        self.__syncFile(
            "history",
            Helpviewer.HelpWindow.HelpWindow.historyManager().getFileName())

    def syncPasswords(self):
        """
        Public method to synchronize the passwords.
        """
        self.__syncFile(
            "passwords",
            Helpviewer.HelpWindow.HelpWindow.passwordManager().getFileName())

    def syncUserAgents(self):
        """
        Public method to synchronize the user agents.
        """
        self.__syncFile(
            "useragents",
            Helpviewer.HelpWindow.HelpWindow.userAgentsManager().getFileName())

    def syncSpeedDial(self):
        """
        Public method to synchronize the speed dial data.
        """
        self.__syncFile(
            "speeddial",
            Helpviewer.HelpWindow.HelpWindow.speedDial().getFileName())

    def shutdown(self):
        """
        Public method to shut down the handler.
        """
        if self.__idleTimer.isActive():
            self.__idleTimer.stop()

        try:
            if self.__connected:
                self.__ftp.quit()
        except ftplib.all_errors:
            pass  # ignore FTP errors because we are shutting down anyway
        self.__connected = False

    def __idleTimeout(self):
        """
        Private slot to prevent a disconnect from the server.
        """
        if self.__state == "idle" and self.__connected:
            try:
                self.__ftp.voidcmd("NOOP")
            except ftplib.Error as err:
                code = err.args[0].strip()[:3]
                if code == "421":
                    self.__connected = False
            except IOError:
                self.__connected = False
示例#7
0
class FtpReply(QNetworkReply):
    """
    Class implementing a network reply for FTP resources.
    """
    def __init__(self, url, accessHandler, parent=None):
        """
        Constructor
        
        @param url requested FTP URL (QUrl)
        @param accessHandler reference to the access handler (FtpAccessHandler)
        @param parent reference to the parent object (QObject)
        """
        super(FtpReply, self).__init__(parent)
        
        self.__manager = parent
        self.__handler = accessHandler
        
        self.__ftp = E5Ftp()
        
        self.__items = []
        self.__content = QByteArray()
        self.__units = ["Bytes", "KB", "MB", "GB", "TB",
                        "PB", "EB", "ZB", "YB"]
        self.__dirLineParser = FtpDirLineParser()
        self.__fileBytesReceived = 0
        
        if url.path() == "":
            url.setPath("/")
        self.setUrl(url)
        
        # do proxy setup
        if not Preferences.getUI("UseProxy"):
            proxyType = E5FtpProxyType.NoProxy
        else:
            proxyType = Preferences.getUI("ProxyType/Ftp")
        if proxyType != E5FtpProxyType.NoProxy:
            self.__ftp.setProxy(
                proxyType,
                Preferences.getUI("ProxyHost/Ftp"),
                Preferences.getUI("ProxyPort/Ftp"))
            if proxyType != E5FtpProxyType.NonAuthorizing:
                self.__ftp.setProxyAuthentication(
                    Preferences.getUI("ProxyUser/Ftp"),
                    Preferences.getUI("ProxyPassword/Ftp"),
                    Preferences.getUI("ProxyAccount/Ftp"))
        
        QTimer.singleShot(0, self.__doFtpCommands)
    
    def abort(self):
        """
        Public slot to abort the operation.
        """
        # do nothing
        pass
    
    def bytesAvailable(self):
        """
        Public method to determined the bytes available for being read.
        
        @return bytes available (integer)
        """
        return self.__content.size()
    
    def isSequential(self):
        """
        Public method to check for sequential access.
        
        @return flag indicating sequential access (boolean)
        """
        return True
    
    def readData(self, maxlen):
        """
        Public method to retrieve data from the reply object.
        
        @param maxlen maximum number of bytes to read (integer)
        @return string containing the data (bytes)
        """
        if self.__content.size():
            len_ = min(maxlen, self.__content.size())
            buffer = bytes(self.__content[:len_])
            self.__content.remove(0, len_)
            return buffer
    
    def __doFtpCommands(self):
        """
        Private slot doing the sequence of FTP commands to get the requested
        result.
        """
        retry = True
        try:
            username = self.url().userName()
            password = self.url().password()
            byAuth = False
            
            while retry:
                try:
                    self.__ftp.connect(self.url().host(),
                                       self.url().port(ftplib.FTP_PORT),
                                       timeout=10)
                except E5FtpProxyError as err:
                    self.setError(QNetworkReply.ProxyNotFoundError, str(err))
                    self.error.emit(QNetworkReply.ProxyNotFoundError)
                    self.finished.emit()
                ok, retry = self.__doFtpLogin(username, password, byAuth)
                if not ok and retry:
                    auth = self.__handler.getAuthenticator(self.url().host())
                    if auth and not auth.isNull() and auth.user():
                        username = auth.user()
                        password = auth.password()
                        byAuth = True
                    else:
                        retry = False
            if ok:
                self.__ftp.retrlines("LIST " + self.url().path(),
                                     self.__dirCallback)
                if len(self.__items) == 1 and \
                   self.__items[0].isFile():
                    self.__fileBytesReceived = 0
                    self.__setContent()
                    self.__ftp.retrbinary(
                        "RETR " + self.url().path(), self.__retrCallback)
                    self.__content.append(512 * b' ')
                    self.readyRead.emit()
                else:
                    self.__setListContent()
                self.__ftp.quit()
        except ftplib.all_errors as err:
            if isinstance(err, socket.gaierror):
                errCode = QNetworkReply.HostNotFoundError
            elif isinstance(err, socket.error) and \
                    err.errno == errno.ECONNREFUSED:
                errCode = QNetworkReply.ConnectionRefusedError
            else:
                errCode = QNetworkReply.ProtocolFailure
            self.setError(errCode, str(err))
            self.error.emit(errCode)
        self.finished.emit()
    
    def __doFtpLogin(self, username, password, byAuth=False):
        """
        Private method to do the FTP login with asking for a username and
        password, if the login fails with an error 530.
        
        @param username user name to use for the login (string)
        @param password password to use for the login (string)
        @param byAuth flag indicating that the login data was provided by an
            authenticator (boolean)
        @return tuple of two flags indicating a successful login and
            if the login should be retried (boolean, boolean)
        """
        try:
            self.__ftp.login(username, password)
            return True, False
        except E5FtpProxyError as err:
            code = str(err)[:3]
            if code[1] == "5":
                # could be a 530, check second line
                lines = str(err).splitlines()
                if lines[1][:3] == "530":
                    if "usage" in "\n".join(lines[1:].lower()):
                        # found a not supported proxy
                        self.setError(
                            QNetworkReply.ProxyConnectionRefusedError,
                            self.tr("The proxy type seems to be wrong."
                                    " If it is not in the list of"
                                    " supported proxy types please report"
                                    " it with the instructions given by"
                                    " the proxy.\n{0}").format(
                                "\n".join(lines[1:])))
                        self.error.emit(
                            QNetworkReply.ProxyConnectionRefusedError)
                        return False, False
                    else:
                        from UI.AuthenticationDialog import \
                            AuthenticationDialog
                        info = self.tr(
                            "<b>Connect to proxy '{0}' using:</b>")\
                            .format(Utilities.html_encode(
                                Preferences.getUI("ProxyHost/Ftp")))
                        dlg = AuthenticationDialog(
                            info, Preferences.getUI("ProxyUser/Ftp"), True)
                        dlg.setData(Preferences.getUI("ProxyUser/Ftp"),
                                    Preferences.getUI("ProxyPassword/Ftp"))
                        if dlg.exec_() == QDialog.Accepted:
                            username, password = dlg.getData()
                            if dlg.shallSave():
                                Preferences.setUI("ProxyUser/Ftp", username)
                                Preferences.setUI(
                                    "ProxyPassword/Ftp", password)
                            self.__ftp.setProxyAuthentication(username,
                                                              password)
                            return False, True
            return False, False
        except (ftplib.error_perm, ftplib.error_temp) as err:
            code = err.args[0].strip()[:3]
            if code in ["530", "421"]:
                # error 530 -> Login incorrect
                # error 421 -> Login may be incorrect (reported by some
                # proxies)
                if byAuth:
                    self.__handler.setAuthenticator(self.url().host(), None)
                    auth = None
                else:
                    auth = self.__handler.getAuthenticator(self.url().host())
                if not auth or auth.isNull() or not auth.user():
                    auth = QAuthenticator()
                    self.__manager.authenticationRequired.emit(self, auth)
                    if not auth.isNull():
                        if auth.user():
                            self.__handler.setAuthenticator(self.url().host(),
                                                            auth)
                            return False, True
                    return False, False
                return False, True
            else:
                raise
    
    def __dirCallback(self, line):
        """
        Private slot handling the receipt of directory listings.
        
        @param line the received line of the directory listing (string)
        """
        try:
            urlInfo = self.__dirLineParser.parseLine(line)
        except FtpDirLineParserError:
            # silently ignore parser errors
            urlInfo = None
        
        if urlInfo:
            self.__items.append(urlInfo)
        
        QCoreApplication.processEvents()
    
    def __retrCallback(self, data):
        """
        Private slot handling the reception of data.
        
        @param data data received from the FTP server (bytes)
        """
        self.__content += QByteArray(data)
        self.__fileBytesReceived += len(data)
        self.downloadProgress.emit(
            self.__fileBytesReceived, self.__items[0].size())
        self.readyRead.emit()
        
        QCoreApplication.processEvents()
    
    def __setContent(self):
        """
        Private method to finish the setup of the data.
        """
        mtype, encoding = mimetypes.guess_type(self.url().toString())
        self.open(QIODevice.ReadOnly | QIODevice.Unbuffered)
        self.setHeader(QNetworkRequest.ContentLengthHeader,
                       self.__items[0].size())
        if mtype:
            self.setHeader(QNetworkRequest.ContentTypeHeader, mtype)
        self.setAttribute(QNetworkRequest.HttpStatusCodeAttribute, 200)
        self.setAttribute(QNetworkRequest.HttpReasonPhraseAttribute, "Ok")
        self.metaDataChanged.emit()
    
    def __cssLinkClass(self, icon, size=32):
        """
        Private method to generate a link class with an icon.
        
        @param icon icon to be included (QIcon)
        @param size size of the icon to be generated (integer)
        @return CSS class string (string)
        """
        cssString = \
            """a.{{0}} {{{{\n"""\
            """  padding-left: {0}px;\n"""\
            """  background: transparent url(data:image/png;base64,{1})"""\
            """ no-repeat center left;\n"""\
            """  font-weight: bold;\n"""\
            """}}}}\n"""
        pixmap = icon.pixmap(size, size)
        imageBuffer = QBuffer()
        imageBuffer.open(QIODevice.ReadWrite)
        if not pixmap.save(imageBuffer, "PNG"):
            # write a blank pixmap on error
            pixmap = QPixmap(size, size)
            pixmap.fill(Qt.transparent)
            imageBuffer.buffer().clear()
            pixmap.save(imageBuffer, "PNG")
        return cssString.format(
            size + 4,
            str(imageBuffer.buffer().toBase64(), encoding="ascii"))
    
    def __setListContent(self):
        """
        Private method to prepare the content for the reader.
        """
        u = self.url()
        if not u.path().endswith("/"):
            u.setPath(u.path() + "/")
        
        baseUrl = self.url().toString()
        basePath = u.path()
        
        linkClasses = {}
        iconSize = QWebSettings.globalSettings().fontSize(
            QWebSettings.DefaultFontSize)
        
        parent = u.resolved(QUrl(".."))
        if parent.isParentOf(u):
            icon = UI.PixmapCache.getIcon("up.png")
            linkClasses["link_parent"] = \
                self.__cssLinkClass(icon, iconSize).format("link_parent")
            parentStr = self.tr(
                """  <p><a class="link_parent" href="{0}">"""
                """Change to parent directory</a></p>"""
            ).format(parent.toString())
        else:
            parentStr = ""
        
        row = \
            """    <tr class="{0}">"""\
            """<td class="name"><a class="{1}" href="{2}">{3}</a></td>"""\
            """<td class="size">{4}</td>"""\
            """<td class="modified">{5}</td>"""\
            """</tr>\n"""
        table = self.tr(
            """    <tr>"""
            """<th align="left">Name</th>"""
            """<th>Size</th>"""
            """<th align="left">Last modified</th>"""
            """</tr>\n"""
        )
        
        i = 0
        for item in self.__items:
            name = item.name()
            if item.isDir() and not name.endswith("/"):
                name += "/"
            child = u.resolved(QUrl(name.replace(":", "%3A")))
            
            if item.isFile():
                size = item.size()
                unit = 0
                while size:
                    newSize = size // 1024
                    if newSize and unit < len(self.__units):
                        size = newSize
                        unit += 1
                    else:
                        break
                
                sizeStr = self.tr("{0} {1}", "size unit")\
                    .format(size, self.__units[unit])
                linkClass = "link_file"
                if linkClass not in linkClasses:
                    icon = UI.PixmapCache.getIcon("fileMisc.png")
                    linkClasses[linkClass] = \
                        self.__cssLinkClass(icon, iconSize).format(linkClass)
            else:
                sizeStr = ""
                linkClass = "link_dir"
                if linkClass not in linkClasses:
                    icon = UI.PixmapCache.getIcon("dirClosed.png")
                    linkClasses[linkClass] = \
                        self.__cssLinkClass(icon, iconSize).format(linkClass)
            table += row.format(
                i == 0 and "odd" or "even",
                linkClass,
                child.toString(),
                Utilities.html_encode(item.name()),
                sizeStr,
                item.lastModified().toString("yyyy-MM-dd hh:mm"),
            )
            i = 1 - i
        
        content = ftpListPage_html.format(
            Utilities.html_encode(baseUrl),
            "".join(linkClasses.values()),
            self.tr("Listing of {0}").format(basePath),
            parentStr,
            table
        )
        self.__content = QByteArray(content.encode("utf8"))
        self.__content.append(512 * b' ')
        
        self.open(QIODevice.ReadOnly | QIODevice.Unbuffered)
        self.setHeader(
            QNetworkRequest.ContentTypeHeader, "text/html; charset=UTF-8")
        self.setHeader(
            QNetworkRequest.ContentLengthHeader, self.__content.size())
        self.setAttribute(QNetworkRequest.HttpStatusCodeAttribute, 200)
        self.setAttribute(QNetworkRequest.HttpReasonPhraseAttribute, "Ok")
        self.metaDataChanged.emit()
        self.downloadProgress.emit(
            self.__content.size(), self.__content.size())
        self.readyRead.emit()
示例#8
0
class FtpSyncHandler(SyncHandler):
    """
    Class implementing a synchronization handler using FTP.
    
    @signal syncStatus(type_, message) emitted to indicate the synchronization
        status (string one of "bookmarks", "history", "passwords",
        "useragents" or "speeddial", string)
    @signal syncError(message) emitted for a general error with the error
        message (string)
    @signal syncMessage(message) emitted to send a message about
        synchronization (string)
    @signal syncFinished(type_, done, download) emitted after a
        synchronization has finished (string one of "bookmarks", "history",
        "passwords", "useragents" or "speeddial", boolean, boolean)
    """
    syncStatus = pyqtSignal(str, str)
    syncError = pyqtSignal(str)
    syncMessage = pyqtSignal(str)
    syncFinished = pyqtSignal(str, bool, bool)
    
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent object (QObject)
        """
        super(FtpSyncHandler, self).__init__(parent)
        
        self.__state = "idle"
        self.__forceUpload = False
        self.__connected = False
        
        self.__remoteFilesFound = {}
    
    def initialLoadAndCheck(self, forceUpload):
        """
        Public method to do the initial check.
        
        @keyparam forceUpload flag indicating a forced upload of the files
            (boolean)
        """
        if not Preferences.getHelp("SyncEnabled"):
            return
        
        self.__state = "initializing"
        self.__forceUpload = forceUpload
        
        self.__dirLineParser = FtpDirLineParser()
        self.__remoteFilesFound = {}
        
        self.__idleTimer = QTimer(self)
        self.__idleTimer.setInterval(
            Preferences.getHelp("SyncFtpIdleTimeout") * 1000)
        self.__idleTimer.timeout.connect(self.__idleTimeout)
        
        self.__ftp = E5Ftp()
        
        # do proxy setup
        if not Preferences.getUI("UseProxy"):
            proxyType = E5FtpProxyType.NoProxy
        else:
            proxyType = Preferences.getUI("ProxyType/Ftp")
        if proxyType != E5FtpProxyType.NoProxy:
            self.__ftp.setProxy(
                proxyType,
                Preferences.getUI("ProxyHost/Ftp"),
                Preferences.getUI("ProxyPort/Ftp"))
            if proxyType != E5FtpProxyType.NonAuthorizing:
                self.__ftp.setProxyAuthentication(
                    Preferences.getUI("ProxyUser/Ftp"),
                    Preferences.getUI("ProxyPassword/Ftp"),
                    Preferences.getUI("ProxyAccount/Ftp"))
        
        QTimer.singleShot(0, self.__doFtpCommands)
    
    def __doFtpCommands(self):
        """
        Private slot executing the sequence of FTP commands.
        """
        try:
            ok = self.__connectAndLogin()
            if ok:
                self.__changeToStore()
                self.__ftp.retrlines("LIST", self.__dirListCallback)
                self.__initialSync()
                self.__state = "idle"
                self.__idleTimer.start()
        except (ftplib.all_errors + (E5FtpProxyError,)) as err:
            self.syncError.emit(str(err))
    
    def __connectAndLogin(self):
        """
        Private method to connect to the FTP server and log in.
        
        @return flag indicating a successful log in (boolean)
        """
        self.__ftp.connect(
            Preferences.getHelp("SyncFtpServer"),
            Preferences.getHelp("SyncFtpPort"),
            timeout=5)
        self.__ftp.login(
            Preferences.getHelp("SyncFtpUser"),
            Preferences.getHelp("SyncFtpPassword"))
        self.__connected = True
        return True
    
    def __changeToStore(self):
        """
        Private slot to change to the storage directory.
        
        This action will create the storage path on the server, if it
        does not exist. Upon return, the current directory of the server
        is the sync directory.
        """
        storePathList = \
            Preferences.getHelp("SyncFtpPath").replace("\\", "/").split("/")
        if storePathList[0] == "":
            storePathList.pop(0)
        while storePathList:
            path = storePathList[0]
            try:
                self.__ftp.cwd(path)
            except ftplib.error_perm as err:
                code = err.args[0].strip()[:3]
                if code == "550":
                    # path does not exist, create it
                    self.__ftp.mkd(path)
                    self.__ftp.cwd(path)
                else:
                    raise
            storePathList.pop(0)
    
    def __dirListCallback(self, line):
        """
        Private slot handling the receipt of directory listing lines.
        
        @param line the received line of the directory listing (string)
        """
        try:
            urlInfo = self.__dirLineParser.parseLine(line)
        except FtpDirLineParserError:
            # silently ignore parser errors
            urlInfo = None
        
        if urlInfo and urlInfo.isValid() and urlInfo.isFile():
            if urlInfo.name() in self._remoteFiles.values():
                self.__remoteFilesFound[urlInfo.name()] = \
                    urlInfo.lastModified()
        
        QCoreApplication.processEvents()
    
    def __downloadFile(self, type_, fileName, timestamp):
        """
        Private method to downlaod the given file.
        
        @param type_ type of the synchronization event (string one
            of "bookmarks", "history", "passwords", "useragents" or
            "speeddial")
        @param fileName name of the file to be downloaded (string)
        @param timestamp time stamp in seconds of the file to be downloaded
            (integer)
        """
        self.syncStatus.emit(type_, self._messages[type_]["RemoteExists"])
        buffer = io.BytesIO()
        try:
            self.__ftp.retrbinary(
                "RETR {0}".format(self._remoteFiles[type_]),
                lambda x: self.__downloadFileCallback(buffer, x))
            ok, error = self.writeFile(
                QByteArray(buffer.getvalue()), fileName, type_, timestamp)
            if not ok:
                self.syncStatus.emit(type_, error)
            self.syncFinished.emit(type_, ok, True)
        except ftplib.all_errors as err:
            self.syncStatus.emit(type_, str(err))
            self.syncFinished.emit(type_, False, True)
    
    def __downloadFileCallback(self, buffer, data):
        """
        Private method receiving the downloaded data.
        
        @param buffer reference to the buffer (io.BytesIO)
        @param data byte string to store in the buffer (bytes)
        @return number of bytes written to the buffer (integer)
        """
        res = buffer.write(data)
        QCoreApplication.processEvents()
        return res
    
    def __uploadFile(self, type_, fileName):
        """
        Private method to upload the given file.
        
        @param type_ type of the synchronization event (string one
            of "bookmarks", "history", "passwords", "useragents" or
            "speeddial")
        @param fileName name of the file to be uploaded (string)
        @return flag indicating success (boolean)
        """
        res = False
        data = self.readFile(fileName, type_)
        if data.isEmpty():
            self.syncStatus.emit(type_, self._messages[type_]["LocalMissing"])
            self.syncFinished.emit(type_, False, False)
        else:
            buffer = io.BytesIO(data.data())
            try:
                self.__ftp.storbinary(
                    "STOR {0}".format(self._remoteFiles[type_]),
                    buffer,
                    callback=lambda x: QCoreApplication.processEvents())
                self.syncFinished.emit(type_, True, False)
                res = True
            except ftplib.all_errors as err:
                self.syncStatus.emit(type_, str(err))
                self.syncFinished.emit(type_, False, False)
        return res
    
    def __initialSyncFile(self, type_, fileName):
        """
        Private method to do the initial synchronization of the given file.
        
        @param type_ type of the synchronization event (string one
            of "bookmarks", "history", "passwords", "useragents" or
            "speeddial")
        @param fileName name of the file to be synchronized (string)
        """
        if not self.__forceUpload and \
           self._remoteFiles[type_] in self.__remoteFilesFound:
            if QFileInfo(fileName).lastModified() < \
               self.__remoteFilesFound[self._remoteFiles[type_]]:
                self.__downloadFile(
                    type_, fileName,
                    self.__remoteFilesFound[self._remoteFiles[type_]]
                        .toTime_t())
            else:
                self.syncStatus.emit(
                    type_, self.tr("No synchronization required."))
                self.syncFinished.emit(type_, True, True)
        else:
            if self._remoteFiles[type_] not in self.__remoteFilesFound:
                self.syncStatus.emit(
                    type_, self._messages[type_]["RemoteMissing"])
            else:
                self.syncStatus.emit(
                    type_, self._messages[type_]["LocalNewer"])
            self.__uploadFile(type_, fileName)
    
    def __initialSync(self):
        """
        Private slot to do the initial synchronization.
        """
        # Bookmarks
        if Preferences.getHelp("SyncBookmarks"):
            self.__initialSyncFile(
                "bookmarks",
                Helpviewer.HelpWindow.HelpWindow.bookmarksManager()
                .getFileName())
        
        # History
        if Preferences.getHelp("SyncHistory"):
            self.__initialSyncFile(
                "history",
                Helpviewer.HelpWindow.HelpWindow.historyManager()
                .getFileName())
        
        # Passwords
        if Preferences.getHelp("SyncPasswords"):
            self.__initialSyncFile(
                "passwords",
                Helpviewer.HelpWindow.HelpWindow.passwordManager()
                .getFileName())
        
        # User Agent Settings
        if Preferences.getHelp("SyncUserAgents"):
            self.__initialSyncFile(
                "useragents",
                Helpviewer.HelpWindow.HelpWindow.userAgentsManager()
                .getFileName())
        
        # Speed Dial Settings
        if Preferences.getHelp("SyncSpeedDial"):
            self.__initialSyncFile(
                "speeddial",
                Helpviewer.HelpWindow.HelpWindow.speedDial().getFileName())
        
        self.__forceUpload = False
    
    def __syncFile(self, type_, fileName):
        """
        Private method to synchronize the given file.
        
        @param type_ type of the synchronization event (string one
            of "bookmarks", "history", "passwords", "useragents" or
            "speeddial")
        @param fileName name of the file to be synchronized (string)
        """
        if self.__state == "initializing":
            return
        
        # use idle timeout to check, if we are still connected
        if self.__connected:
            self.__idleTimeout()
        if not self.__connected or self.__ftp.sock is None:
            ok = self.__connectAndLogin()
            if not ok:
                self.syncStatus.emit(
                    type_, self.tr("Cannot log in to FTP host."))
                return
        
        # upload the changed file
        self.__state = "uploading"
        self.syncStatus.emit(type_, self._messages[type_]["Uploading"])
        if self.__uploadFile(type_, fileName):
            self.syncStatus.emit(
                type_, self.tr("Synchronization finished."))
        self.__state = "idle"
    
    def syncBookmarks(self):
        """
        Public method to synchronize the bookmarks.
        """
        self.__syncFile(
            "bookmarks",
            Helpviewer.HelpWindow.HelpWindow.bookmarksManager().getFileName())
    
    def syncHistory(self):
        """
        Public method to synchronize the history.
        """
        self.__syncFile(
            "history",
            Helpviewer.HelpWindow.HelpWindow.historyManager().getFileName())
    
    def syncPasswords(self):
        """
        Public method to synchronize the passwords.
        """
        self.__syncFile(
            "passwords",
            Helpviewer.HelpWindow.HelpWindow.passwordManager().getFileName())
    
    def syncUserAgents(self):
        """
        Public method to synchronize the user agents.
        """
        self.__syncFile(
            "useragents",
            Helpviewer.HelpWindow.HelpWindow.userAgentsManager().getFileName())
    
    def syncSpeedDial(self):
        """
        Public method to synchronize the speed dial data.
        """
        self.__syncFile(
            "speeddial",
            Helpviewer.HelpWindow.HelpWindow.speedDial().getFileName())
    
    def shutdown(self):
        """
        Public method to shut down the handler.
        """
        if self.__idleTimer.isActive():
            self.__idleTimer.stop()
        
        try:
            if self.__connected:
                self.__ftp.quit()
        except ftplib.all_errors:
            pass    # ignore FTP errors because we are shutting down anyway
        self.__connected = False
    
    def __idleTimeout(self):
        """
        Private slot to prevent a disconnect from the server.
        """
        if self.__state == "idle" and self.__connected:
            try:
                self.__ftp.voidcmd("NOOP")
            except ftplib.Error as err:
                code = err.args[0].strip()[:3]
                if code == "421":
                    self.__connected = False
            except IOError:
                self.__connected = False